Auto merge of #148446 - GuillaumeGomez:rollup-lxwlqol, r=GuillaumeGomez

Rollup of 8 pull requests

Successful merges:

 - rust-lang/rust#135099 (Add FileCheck annotations to mir-opt/copy-prop)
 - rust-lang/rust#145903 (Give correct suggestion for a typo in raw pointers)
 - rust-lang/rust#147520 (Port the remaining SIMD intrinsics to const-eval)
 - rust-lang/rust#148068 (rustdoc: Use configured target modifiers when collecting doctests)
 - rust-lang/rust#148099 (Prepare to move debugger discovery from compiletest to bootstrap)
 - rust-lang/rust#148268 (rustdoc: fix `--emit=dep-info` on scraped examples)
 - rust-lang/rust#148306 (Remove double check when decoding ExpnId to avoid races)
 - rust-lang/rust#148378 (Fix documentation for std::panic::update_hook)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-11-03 16:29:59 +00:00
commit 1f880d9a1f
50 changed files with 1334 additions and 370 deletions

View file

@ -25,6 +25,35 @@ use super::{
};
use crate::fluent_generated as fluent;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum MulAddType {
/// Used with `fma` and `simd_fma`, always uses fused-multiply-add
Fused,
/// Used with `fmuladd` and `simd_relaxed_fma`, nondeterministically determines whether to use
/// fma or simple multiply-add
Nondeterministic,
}
#[derive(Copy, Clone)]
pub(crate) enum MinMax {
/// The IEEE `Minimum` operation - see `f32::minimum` etc
/// In particular, `-0.0` is considered smaller than `+0.0` and
/// if either input is NaN, the result is NaN.
Minimum,
/// The IEEE `MinNum` operation - see `f32::min` etc
/// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic,
/// and is one argument is NaN, the other one is returned.
MinNum,
/// The IEEE `Maximum` operation - see `f32::maximum` etc
/// In particular, `-0.0` is considered smaller than `+0.0` and
/// if either input is NaN, the result is NaN.
Maximum,
/// The IEEE `MaxNum` operation - see `f32::max` etc
/// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic,
/// and is one argument is NaN, the other one is returned.
MaxNum,
}
/// Directly returns an `Allocation` containing an absolute path representation of the given type.
pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) {
let path = crate::util::type_name(tcx, ty);
@ -504,25 +533,33 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?;
}
sym::minnumf16 => self.float_min_intrinsic::<Half>(args, dest)?,
sym::minnumf32 => self.float_min_intrinsic::<Single>(args, dest)?,
sym::minnumf64 => self.float_min_intrinsic::<Double>(args, dest)?,
sym::minnumf128 => self.float_min_intrinsic::<Quad>(args, dest)?,
sym::minnumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::MinNum, dest)?,
sym::minnumf32 => self.float_minmax_intrinsic::<Single>(args, MinMax::MinNum, dest)?,
sym::minnumf64 => self.float_minmax_intrinsic::<Double>(args, MinMax::MinNum, dest)?,
sym::minnumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::MinNum, dest)?,
sym::minimumf16 => self.float_minimum_intrinsic::<Half>(args, dest)?,
sym::minimumf32 => self.float_minimum_intrinsic::<Single>(args, dest)?,
sym::minimumf64 => self.float_minimum_intrinsic::<Double>(args, dest)?,
sym::minimumf128 => self.float_minimum_intrinsic::<Quad>(args, dest)?,
sym::minimumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::Minimum, dest)?,
sym::minimumf32 => {
self.float_minmax_intrinsic::<Single>(args, MinMax::Minimum, dest)?
}
sym::minimumf64 => {
self.float_minmax_intrinsic::<Double>(args, MinMax::Minimum, dest)?
}
sym::minimumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::Minimum, dest)?,
sym::maxnumf16 => self.float_max_intrinsic::<Half>(args, dest)?,
sym::maxnumf32 => self.float_max_intrinsic::<Single>(args, dest)?,
sym::maxnumf64 => self.float_max_intrinsic::<Double>(args, dest)?,
sym::maxnumf128 => self.float_max_intrinsic::<Quad>(args, dest)?,
sym::maxnumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::MaxNum, dest)?,
sym::maxnumf32 => self.float_minmax_intrinsic::<Single>(args, MinMax::MaxNum, dest)?,
sym::maxnumf64 => self.float_minmax_intrinsic::<Double>(args, MinMax::MaxNum, dest)?,
sym::maxnumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::MaxNum, dest)?,
sym::maximumf16 => self.float_maximum_intrinsic::<Half>(args, dest)?,
sym::maximumf32 => self.float_maximum_intrinsic::<Single>(args, dest)?,
sym::maximumf64 => self.float_maximum_intrinsic::<Double>(args, dest)?,
sym::maximumf128 => self.float_maximum_intrinsic::<Quad>(args, dest)?,
sym::maximumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::Maximum, dest)?,
sym::maximumf32 => {
self.float_minmax_intrinsic::<Single>(args, MinMax::Maximum, dest)?
}
sym::maximumf64 => {
self.float_minmax_intrinsic::<Double>(args, MinMax::Maximum, dest)?
}
sym::maximumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::Maximum, dest)?,
sym::copysignf16 => self.float_copysign_intrinsic::<Half>(args, dest)?,
sym::copysignf32 => self.float_copysign_intrinsic::<Single>(args, dest)?,
@ -630,14 +667,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
dest,
rustc_apfloat::Round::NearestTiesToEven,
)?,
sym::fmaf16 => self.fma_intrinsic::<Half>(args, dest)?,
sym::fmaf32 => self.fma_intrinsic::<Single>(args, dest)?,
sym::fmaf64 => self.fma_intrinsic::<Double>(args, dest)?,
sym::fmaf128 => self.fma_intrinsic::<Quad>(args, dest)?,
sym::fmuladdf16 => self.float_muladd_intrinsic::<Half>(args, dest)?,
sym::fmuladdf32 => self.float_muladd_intrinsic::<Single>(args, dest)?,
sym::fmuladdf64 => self.float_muladd_intrinsic::<Double>(args, dest)?,
sym::fmuladdf128 => self.float_muladd_intrinsic::<Quad>(args, dest)?,
sym::fmaf16 => self.float_muladd_intrinsic::<Half>(args, dest, MulAddType::Fused)?,
sym::fmaf32 => self.float_muladd_intrinsic::<Single>(args, dest, MulAddType::Fused)?,
sym::fmaf64 => self.float_muladd_intrinsic::<Double>(args, dest, MulAddType::Fused)?,
sym::fmaf128 => self.float_muladd_intrinsic::<Quad>(args, dest, MulAddType::Fused)?,
sym::fmuladdf16 => {
self.float_muladd_intrinsic::<Half>(args, dest, MulAddType::Nondeterministic)?
}
sym::fmuladdf32 => {
self.float_muladd_intrinsic::<Single>(args, dest, MulAddType::Nondeterministic)?
}
sym::fmuladdf64 => {
self.float_muladd_intrinsic::<Double>(args, dest, MulAddType::Nondeterministic)?
}
sym::fmuladdf128 => {
self.float_muladd_intrinsic::<Quad>(args, dest, MulAddType::Nondeterministic)?
}
// Unsupported intrinsic: skip the return_to_block below.
_ => return interp_ok(false),
@ -919,76 +964,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
interp_ok(Scalar::from_bool(lhs_bytes == rhs_bytes))
}
fn float_min_intrinsic<F>(
&mut self,
args: &[OpTy<'tcx, M::Provenance>],
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()>
fn float_minmax<F>(
&self,
a: Scalar<M::Provenance>,
b: Scalar<M::Provenance>,
op: MinMax,
) -> InterpResult<'tcx, Scalar<M::Provenance>>
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let res = if a == b {
let a: F = a.to_float()?;
let b: F = b.to_float()?;
let res = if matches!(op, MinMax::MinNum | MinMax::MaxNum) && a == b {
// They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
// Let the machine decide which one to return.
M::equal_float_min_max(self, a, b)
} else {
self.adjust_nan(a.min(b), &[a, b])
let result = match op {
MinMax::Minimum => a.minimum(b),
MinMax::MinNum => a.min(b),
MinMax::Maximum => a.maximum(b),
MinMax::MaxNum => a.max(b),
};
self.adjust_nan(result, &[a, b])
};
self.write_scalar(res, dest)?;
interp_ok(())
interp_ok(res.into())
}
fn float_max_intrinsic<F>(
fn float_minmax_intrinsic<F>(
&mut self,
args: &[OpTy<'tcx, M::Provenance>],
op: MinMax,
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()>
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let res = if a == b {
// They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
// Let the machine decide which one to return.
M::equal_float_min_max(self, a, b)
} else {
self.adjust_nan(a.max(b), &[a, b])
};
self.write_scalar(res, dest)?;
interp_ok(())
}
fn float_minimum_intrinsic<F>(
&mut self,
args: &[OpTy<'tcx, M::Provenance>],
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()>
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let res = a.minimum(b);
let res = self.adjust_nan(res, &[a, b]);
self.write_scalar(res, dest)?;
interp_ok(())
}
fn float_maximum_intrinsic<F>(
&mut self,
args: &[OpTy<'tcx, M::Provenance>],
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()>
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let res = a.maximum(b);
let res = self.adjust_nan(res, &[a, b]);
let res =
self.float_minmax::<F>(self.read_scalar(&args[0])?, self.read_scalar(&args[1])?, op)?;
self.write_scalar(res, dest)?;
interp_ok(())
}
@ -1022,6 +1036,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
interp_ok(())
}
fn float_round<F>(
&mut self,
x: Scalar<M::Provenance>,
mode: rustc_apfloat::Round,
) -> InterpResult<'tcx, Scalar<M::Provenance>>
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let x: F = x.to_float()?;
let res = x.round_to_integral(mode).value;
let res = self.adjust_nan(res, &[x]);
interp_ok(res.into())
}
fn float_round_intrinsic<F>(
&mut self,
args: &[OpTy<'tcx, M::Provenance>],
@ -1031,47 +1059,46 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let x: F = self.read_scalar(&args[0])?.to_float()?;
let res = x.round_to_integral(mode).value;
let res = self.adjust_nan(res, &[x]);
let res = self.float_round::<F>(self.read_scalar(&args[0])?, mode)?;
self.write_scalar(res, dest)?;
interp_ok(())
}
fn fma_intrinsic<F>(
&mut self,
args: &[OpTy<'tcx, M::Provenance>],
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()>
fn float_muladd<F>(
&self,
a: Scalar<M::Provenance>,
b: Scalar<M::Provenance>,
c: Scalar<M::Provenance>,
typ: MulAddType,
) -> InterpResult<'tcx, Scalar<M::Provenance>>
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let c: F = self.read_scalar(&args[2])?.to_float()?;
let a: F = a.to_float()?;
let b: F = b.to_float()?;
let c: F = c.to_float()?;
let res = a.mul_add(b, c).value;
let fuse = typ == MulAddType::Fused || M::float_fuse_mul_add(self);
let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
let res = self.adjust_nan(res, &[a, b, c]);
self.write_scalar(res, dest)?;
interp_ok(())
interp_ok(res.into())
}
fn float_muladd_intrinsic<F>(
&mut self,
args: &[OpTy<'tcx, M::Provenance>],
dest: &PlaceTy<'tcx, M::Provenance>,
typ: MulAddType,
) -> InterpResult<'tcx, ()>
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let c: F = self.read_scalar(&args[2])?.to_float()?;
let a = self.read_scalar(&args[0])?;
let b = self.read_scalar(&args[1])?;
let c = self.read_scalar(&args[2])?;
let fuse = M::float_fuse_mul_add(self);
let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
let res = self.adjust_nan(res, &[a, b, c]);
let res = self.float_muladd::<F>(a, b, c, typ)?;
self.write_scalar(res, dest)?;
interp_ok(())
}

View file

@ -1,5 +1,6 @@
use either::Either;
use rustc_abi::Endian;
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
use rustc_apfloat::{Float, Round};
use rustc_middle::mir::interpret::{InterpErrorKind, UndefinedBehaviorInfo};
use rustc_middle::ty::FloatTy;
@ -8,17 +9,11 @@ use rustc_span::{Symbol, sym};
use tracing::trace;
use super::{
ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Provenance, Scalar, Size, interp_ok,
throw_ub_format,
ImmTy, InterpCx, InterpResult, Machine, MinMax, MulAddType, OpTy, PlaceTy, Provenance, Scalar,
Size, interp_ok, throw_ub_format,
};
use crate::interpret::Writeable;
#[derive(Copy, Clone)]
pub(crate) enum MinMax {
Min,
Max,
}
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// Returns `true` if emulation happened.
/// Here we implement the intrinsics that are common to all CTFE instances; individual machines can add their own
@ -125,10 +120,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let op = op.to_scalar();
// "Bitwise" operation, no NaN adjustments
match float_ty {
FloatTy::F16 => unimplemented!("f16_f128"),
FloatTy::F16 => Scalar::from_f16(op.to_f16()?.abs()),
FloatTy::F32 => Scalar::from_f32(op.to_f32()?.abs()),
FloatTy::F64 => Scalar::from_f64(op.to_f64()?.abs()),
FloatTy::F128 => unimplemented!("f16_f128"),
FloatTy::F128 => Scalar::from_f128(op.to_f128()?.abs()),
}
}
Op::Round(rounding) => {
@ -139,21 +134,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
intrinsic_name
)
};
let op = op.to_scalar();
match float_ty {
FloatTy::F16 => unimplemented!("f16_f128"),
FloatTy::F32 => {
let f = op.to_scalar().to_f32()?;
let res = f.round_to_integral(rounding).value;
let res = self.adjust_nan(res, &[f]);
Scalar::from_f32(res)
}
FloatTy::F64 => {
let f = op.to_scalar().to_f64()?;
let res = f.round_to_integral(rounding).value;
let res = self.adjust_nan(res, &[f]);
Scalar::from_f64(res)
}
FloatTy::F128 => unimplemented!("f16_f128"),
FloatTy::F16 => self.float_round::<Half>(op, rounding)?,
FloatTy::F32 => self.float_round::<Single>(op, rounding)?,
FloatTy::F64 => self.float_round::<Double>(op, rounding)?,
FloatTy::F128 => self.float_round::<Quad>(op, rounding)?,
}
}
Op::Numeric(name) => {
@ -216,8 +202,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
sym::simd_le => Op::MirOp(BinOp::Le),
sym::simd_gt => Op::MirOp(BinOp::Gt),
sym::simd_ge => Op::MirOp(BinOp::Ge),
sym::simd_fmax => Op::FMinMax(MinMax::Max),
sym::simd_fmin => Op::FMinMax(MinMax::Min),
sym::simd_fmax => Op::FMinMax(MinMax::MaxNum),
sym::simd_fmin => Op::FMinMax(MinMax::MinNum),
sym::simd_saturating_add => Op::SaturatingOp(BinOp::Add),
sym::simd_saturating_sub => Op::SaturatingOp(BinOp::Sub),
sym::simd_arith_offset => Op::WrappingOffset,
@ -309,8 +295,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
sym::simd_reduce_xor => Op::MirOp(BinOp::BitXor),
sym::simd_reduce_any => Op::MirOpBool(BinOp::BitOr),
sym::simd_reduce_all => Op::MirOpBool(BinOp::BitAnd),
sym::simd_reduce_max => Op::MinMax(MinMax::Max),
sym::simd_reduce_min => Op::MinMax(MinMax::Min),
sym::simd_reduce_max => Op::MinMax(MinMax::MaxNum),
sym::simd_reduce_min => Op::MinMax(MinMax::MinNum),
_ => unreachable!(),
};
@ -332,10 +318,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if matches!(res.layout.ty.kind(), ty::Float(_)) {
ImmTy::from_scalar(self.fminmax_op(mmop, &res, &op)?, res.layout)
} else {
// Just boring integers, so NaNs to worry about
// Just boring integers, no NaNs to worry about.
let mirop = match mmop {
MinMax::Min => BinOp::Le,
MinMax::Max => BinOp::Ge,
MinMax::MinNum | MinMax::Minimum => BinOp::Le,
MinMax::MaxNum | MinMax::Maximum => BinOp::Ge,
};
if self.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? {
res
@ -701,6 +687,43 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
};
}
}
sym::simd_fma | sym::simd_relaxed_fma => {
// `simd_fma` should always deterministically use `mul_add`, whereas `relaxed_fma`
// is non-deterministic, and can use either `mul_add` or `a * b + c`
let typ = match intrinsic_name {
sym::simd_fma => MulAddType::Fused,
sym::simd_relaxed_fma => MulAddType::Nondeterministic,
_ => unreachable!(),
};
let (a, a_len) = self.project_to_simd(&args[0])?;
let (b, b_len) = self.project_to_simd(&args[1])?;
let (c, c_len) = self.project_to_simd(&args[2])?;
let (dest, dest_len) = self.project_to_simd(&dest)?;
assert_eq!(dest_len, a_len);
assert_eq!(dest_len, b_len);
assert_eq!(dest_len, c_len);
for i in 0..dest_len {
let a = self.read_scalar(&self.project_index(&a, i)?)?;
let b = self.read_scalar(&self.project_index(&b, i)?)?;
let c = self.read_scalar(&self.project_index(&c, i)?)?;
let dest = self.project_index(&dest, i)?;
let ty::Float(float_ty) = dest.layout.ty.kind() else {
span_bug!(self.cur_span(), "{} operand is not a float", intrinsic_name)
};
let val = match float_ty {
FloatTy::F16 => self.float_muladd::<Half>(a, b, c, typ)?,
FloatTy::F32 => self.float_muladd::<Single>(a, b, c, typ)?,
FloatTy::F64 => self.float_muladd::<Double>(a, b, c, typ)?,
FloatTy::F128 => self.float_muladd::<Quad>(a, b, c, typ)?,
};
self.write_scalar(val, &dest)?;
}
}
// Unsupported intrinsic: skip the return_to_block below.
_ => return interp_ok(false),
@ -711,12 +734,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
interp_ok(true)
}
fn fminmax_op<Prov: Provenance>(
fn fminmax_op(
&self,
op: MinMax,
left: &ImmTy<'tcx, Prov>,
right: &ImmTy<'tcx, Prov>,
) -> InterpResult<'tcx, Scalar<Prov>> {
left: &ImmTy<'tcx, M::Provenance>,
right: &ImmTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
assert_eq!(left.layout.ty, right.layout.ty);
let ty::Float(float_ty) = left.layout.ty.kind() else {
bug!("fmax operand is not a float")
@ -724,28 +747,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let left = left.to_scalar();
let right = right.to_scalar();
interp_ok(match float_ty {
FloatTy::F16 => unimplemented!("f16_f128"),
FloatTy::F32 => {
let left = left.to_f32()?;
let right = right.to_f32()?;
let res = match op {
MinMax::Min => left.min(right),
MinMax::Max => left.max(right),
};
let res = self.adjust_nan(res, &[left, right]);
Scalar::from_f32(res)
}
FloatTy::F64 => {
let left = left.to_f64()?;
let right = right.to_f64()?;
let res = match op {
MinMax::Min => left.min(right),
MinMax::Max => left.max(right),
};
let res = self.adjust_nan(res, &[left, right]);
Scalar::from_f64(res)
}
FloatTy::F128 => unimplemented!("f16_f128"),
FloatTy::F16 => self.float_minmax::<Half>(left, right, op)?,
FloatTy::F32 => self.float_minmax::<Single>(left, right, op)?,
FloatTy::F64 => self.float_minmax::<Double>(left, right, op)?,
FloatTy::F128 => self.float_minmax::<Quad>(left, right, op)?,
})
}
}

View file

@ -290,7 +290,7 @@ pub trait Machine<'tcx>: Sized {
}
/// Determines whether the `fmuladd` intrinsics fuse the multiply-add or use separate operations.
fn float_fuse_mul_add(_ecx: &mut InterpCx<'tcx, Self>) -> bool;
fn float_fuse_mul_add(_ecx: &InterpCx<'tcx, Self>) -> bool;
/// Called before a basic block terminator is executed.
#[inline]
@ -676,7 +676,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
}
#[inline(always)]
fn float_fuse_mul_add(_ecx: &mut InterpCx<$tcx, Self>) -> bool {
fn float_fuse_mul_add(_ecx: &InterpCx<$tcx, Self>) -> bool {
true
}

View file

@ -1560,7 +1560,6 @@ impl<'a> CrateMetadataRef<'a> {
}
fn expn_hash_to_expn_id(self, sess: &Session, index_guess: u32, hash: ExpnHash) -> ExpnId {
debug_assert_eq!(ExpnId::from_hash(hash), None);
let index_guess = ExpnIndex::from_u32(index_guess);
let old_hash = self.root.expn_hashes.get(self, index_guess).map(|lazy| lazy.decode(self));

View file

@ -407,6 +407,10 @@ impl<'a> Parser<'a> {
// Qualified path
let (qself, path) = self.parse_qpath(PathStyle::Type)?;
TyKind::Path(Some(qself), path)
} else if (self.token.is_keyword(kw::Const) || self.token.is_keyword(kw::Mut))
&& self.look_ahead(1, |t| *t == token::Star)
{
self.parse_ty_c_style_pointer()?
} else if self.check_path() {
self.parse_path_start_ty(lo, allow_plus, ty_generics)?
} else if self.can_begin_bound() {
@ -588,6 +592,41 @@ impl<'a> Parser<'a> {
Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
}
/// Parses a raw pointer with a C-style typo
fn parse_ty_c_style_pointer(&mut self) -> PResult<'a, TyKind> {
let kw_span = self.token.span;
let mutbl = self.parse_const_or_mut();
if let Some(mutbl) = mutbl
&& self.eat(exp!(Star))
{
let star_span = self.prev_token.span;
let mutability = match mutbl {
Mutability::Not => "const",
Mutability::Mut => "mut",
};
let ty = self.parse_ty_no_question_mark_recover()?;
self.dcx()
.struct_span_err(
kw_span,
format!("raw pointer types must be written as `*{mutability} T`"),
)
.with_multipart_suggestion(
format!("put the `*` before `{mutability}`"),
vec![(star_span, String::new()), (kw_span.shrink_to_lo(), "*".to_string())],
Applicability::MachineApplicable,
)
.emit();
return Ok(TyKind::Ptr(MutTy { ty, mutbl }));
}
// This is unreachable because we always get into if above and return from it
unreachable!("this could never happen")
}
/// Parses a raw pointer type: `*[const | mut] $type`.
fn parse_ty_ptr(&mut self) -> PResult<'a, TyKind> {
let mutbl = self.parse_const_or_mut().unwrap_or_else(|| {

View file

@ -215,10 +215,10 @@ pub fn take_hook() -> Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send> {
///
/// // Equivalent to
/// // let prev = panic::take_hook();
/// // panic::set_hook(move |info| {
/// // panic::set_hook(Box::new(move |info| {
/// // println!("...");
/// // prev(info);
/// // );
/// // }));
/// panic::update_hook(move |prev, info| {
/// println!("Print custom message and execute panic handler as usual");
/// prev(info);

View file

@ -29,6 +29,7 @@ use crate::core::builder::{
};
use crate::core::config::TargetSelection;
use crate::core::config::flags::{Subcommand, get_completion, top_level_help};
use crate::core::debuggers;
use crate::utils::build_stamp::{self, BuildStamp};
use crate::utils::exec::{BootstrapCommand, command};
use crate::utils::helpers::{
@ -38,8 +39,6 @@ use crate::utils::helpers::{
use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests};
use crate::{CLang, CodegenBackendKind, DocTests, GitRepo, Mode, PathSet, envify};
const ADB_TEST_DIR: &str = "/data/local/tmp/work";
/// Runs `cargo test` on various internal tools used by bootstrap.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CrateBootstrap {
@ -2078,27 +2077,28 @@ Please disable assertions with `rust.debug-assertions = false`.
cmd.arg("--python").arg(builder.python());
if let Some(ref gdb) = builder.config.gdb {
cmd.arg("--gdb").arg(gdb);
// FIXME(#148099): Currently we set these Android-related flags in all
// modes, even though they should only be needed in "debuginfo" mode,
// because the GDB-discovery code in compiletest currently assumes that
// `--android-cross-path` is always set for Android targets.
if let Some(debuggers::Android { adb_path, adb_test_dir, android_cross_path }) =
debuggers::discover_android(builder, target)
{
cmd.arg("--adb-path").arg(adb_path);
cmd.arg("--adb-test-dir").arg(adb_test_dir);
cmd.arg("--android-cross-path").arg(android_cross_path);
}
let lldb_exe = builder.config.lldb.clone().unwrap_or_else(|| PathBuf::from("lldb"));
let lldb_version = command(&lldb_exe)
.allow_failure()
.arg("--version")
.run_capture(builder)
.stdout_if_ok()
.and_then(|v| if v.trim().is_empty() { None } else { Some(v) });
if let Some(ref vers) = lldb_version {
cmd.arg("--lldb-version").arg(vers);
let lldb_python_dir = command(&lldb_exe)
.allow_failure()
.arg("-P")
.run_capture_stdout(builder)
.stdout_if_ok()
.map(|p| p.lines().next().expect("lldb Python dir not found").to_string());
if let Some(ref dir) = lldb_python_dir {
cmd.arg("--lldb-python-dir").arg(dir);
if mode == "debuginfo" {
if let Some(debuggers::Gdb { gdb }) = debuggers::discover_gdb(builder) {
cmd.arg("--gdb").arg(gdb);
}
if let Some(debuggers::Lldb { lldb_version, lldb_python_dir }) =
debuggers::discover_lldb(builder)
{
cmd.arg("--lldb-version").arg(lldb_version);
cmd.arg("--lldb-python-dir").arg(lldb_python_dir);
}
}
@ -2332,16 +2332,6 @@ Please disable assertions with `rust.debug-assertions = false`.
cmd.env("RUST_TEST_TMPDIR", builder.tempdir());
cmd.arg("--adb-path").arg("adb");
cmd.arg("--adb-test-dir").arg(ADB_TEST_DIR);
if target.contains("android") && !builder.config.dry_run() {
// Assume that cc for this target comes from the android sysroot
cmd.arg("--android-cross-path")
.arg(builder.cc(target).parent().unwrap().parent().unwrap());
} else {
cmd.arg("--android-cross-path").arg("");
}
if builder.config.cmd.rustfix_coverage() {
cmd.arg("--rustfix-coverage");
}

View file

@ -0,0 +1,24 @@
use std::path::PathBuf;
use crate::core::builder::Builder;
use crate::core::config::TargetSelection;
pub(crate) struct Android {
pub(crate) adb_path: &'static str,
pub(crate) adb_test_dir: &'static str,
pub(crate) android_cross_path: PathBuf,
}
pub(crate) fn discover_android(builder: &Builder<'_>, target: TargetSelection) -> Option<Android> {
let adb_path = "adb";
// See <https://github.com/rust-lang/rust/pull/102755>.
let adb_test_dir = "/data/local/tmp/work";
let android_cross_path = if target.contains("android") && !builder.config.dry_run() {
builder.cc(target).parent().unwrap().parent().unwrap().to_owned()
} else {
PathBuf::new()
};
Some(Android { adb_path, adb_test_dir, android_cross_path })
}

View file

@ -0,0 +1,13 @@
use std::path::Path;
use crate::core::builder::Builder;
pub(crate) struct Gdb<'a> {
pub(crate) gdb: &'a Path,
}
pub(crate) fn discover_gdb<'a>(builder: &'a Builder<'_>) -> Option<Gdb<'a>> {
let gdb = builder.config.gdb.as_deref()?;
Some(Gdb { gdb })
}

View file

@ -0,0 +1,32 @@
use std::path::PathBuf;
use crate::core::builder::Builder;
use crate::utils::exec::command;
pub(crate) struct Lldb {
pub(crate) lldb_version: String,
pub(crate) lldb_python_dir: String,
}
pub(crate) fn discover_lldb(builder: &Builder<'_>) -> Option<Lldb> {
// FIXME(#148361): We probably should not be picking up whatever arbitrary
// lldb happens to be in the user's path, and instead require some kind of
// explicit opt-in or configuration.
let lldb_exe = builder.config.lldb.clone().unwrap_or_else(|| PathBuf::from("lldb"));
let lldb_version = command(&lldb_exe)
.allow_failure()
.arg("--version")
.run_capture(builder)
.stdout_if_ok()
.and_then(|v| if v.trim().is_empty() { None } else { Some(v) })?;
let lldb_python_dir = command(&lldb_exe)
.allow_failure()
.arg("-P")
.run_capture_stdout(builder)
.stdout_if_ok()
.map(|p| p.lines().next().expect("lldb Python dir not found").to_string())?;
Some(Lldb { lldb_version, lldb_python_dir })
}

View file

@ -0,0 +1,10 @@
//! Code for discovering debuggers and debugger-related configuration, so that
//! it can be passed to compiletest when running debuginfo tests.
pub(crate) use self::android::{Android, discover_android};
pub(crate) use self::gdb::{Gdb, discover_gdb};
pub(crate) use self::lldb::{Lldb, discover_lldb};
mod android;
mod gdb;
mod lldb;

View file

@ -1,6 +1,7 @@
pub(crate) mod build_steps;
pub(crate) mod builder;
pub(crate) mod config;
pub(crate) mod debuggers;
pub(crate) mod download;
pub(crate) mod metadata;
pub(crate) mod sanity;

View file

@ -166,6 +166,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
remap_path_prefix: options.remap_path_prefix.clone(),
unstable_opts: options.unstable_opts.clone(),
error_format: options.error_format.clone(),
target_modifiers: options.target_modifiers.clone(),
..config::Options::default()
};

View file

@ -896,7 +896,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
// Register the loaded external files in the source map so they show up in depinfo.
// We can't load them via the source map because it gets created after we process the options.
for external_path in &loaded_paths {
let _ = sess.source_map().load_file(external_path);
let _ = sess.source_map().load_binary_file(external_path);
}
if sess.opts.describe_lints {

View file

@ -273,6 +273,7 @@ pub(crate) fn run(
bin_crate: bool,
) {
let inner = move || -> Result<(), String> {
let emit_dep_info = renderopts.dep_info().is_some();
// Generates source files for examples
renderopts.no_emit_shared = true;
let (cx, _) = Context::init(krate, renderopts, cache, tcx, Default::default())
@ -320,6 +321,10 @@ pub(crate) fn run(
calls.encode(&mut encoder);
encoder.finish().map_err(|(_path, e)| e.to_string())?;
if emit_dep_info {
rustc_interface::passes::write_dep_info(tcx);
}
Ok(())
};

View file

@ -103,22 +103,19 @@ fn find_cdb(target: &str) -> Option<Utf8PathBuf> {
}
/// Returns Path to CDB
pub(crate) fn analyze_cdb(
cdb: Option<String>,
target: &str,
) -> (Option<Utf8PathBuf>, Option<[u16; 4]>) {
pub(crate) fn discover_cdb(cdb: Option<String>, target: &str) -> Option<Utf8PathBuf> {
let cdb = cdb.map(Utf8PathBuf::from).or_else(|| find_cdb(target));
cdb
}
pub(crate) fn query_cdb_version(cdb: &Utf8Path) -> Option<[u16; 4]> {
let mut version = None;
if let Some(cdb) = cdb.as_ref() {
if let Ok(output) = Command::new(cdb).arg("/version").output() {
if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
version = extract_cdb_version(&first_line);
}
if let Ok(output) = Command::new(cdb).arg("/version").output() {
if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
version = extract_cdb_version(&first_line);
}
}
(cdb, version)
version
}
pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
@ -132,12 +129,11 @@ pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
Some([major, minor, patch, build])
}
/// Returns (Path to GDB, GDB Version)
pub(crate) fn analyze_gdb(
pub(crate) fn discover_gdb(
gdb: Option<String>,
target: &str,
android_cross_path: &Utf8Path,
) -> (Option<String>, Option<u32>) {
) -> Option<String> {
#[cfg(not(windows))]
const GDB_FALLBACK: &str = "gdb";
#[cfg(windows)]
@ -159,6 +155,10 @@ pub(crate) fn analyze_gdb(
Some(ref s) => s.to_owned(),
};
Some(gdb)
}
pub(crate) fn query_gdb_version(gdb: &str) -> Option<u32> {
let mut version_line = None;
if let Ok(output) = Command::new(&gdb).arg("--version").output() {
if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
@ -168,10 +168,10 @@ pub(crate) fn analyze_gdb(
let version = match version_line {
Some(line) => extract_gdb_version(&line),
None => return (None, None),
None => return None,
};
(Some(gdb), version)
version
}
pub(crate) fn extract_gdb_version(full_version_line: &str) -> Option<u32> {

View file

@ -258,10 +258,11 @@ fn parse_config(args: Vec<String>) -> Config {
let target = opt_str2(matches.opt_str("target"));
let android_cross_path = opt_path(matches, "android-cross-path");
// FIXME: `cdb_version` is *derived* from cdb, but it's *not* technically a config!
let (cdb, cdb_version) = debuggers::analyze_cdb(matches.opt_str("cdb"), &target);
let cdb = debuggers::discover_cdb(matches.opt_str("cdb"), &target);
let cdb_version = cdb.as_deref().and_then(debuggers::query_cdb_version);
// FIXME: `gdb_version` is *derived* from gdb, but it's *not* technically a config!
let (gdb, gdb_version) =
debuggers::analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
let gdb = debuggers::discover_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
let gdb_version = gdb.as_deref().and_then(debuggers::query_gdb_version);
// FIXME: `lldb_version` is *derived* from lldb, but it's *not* technically a config!
let lldb_version =
matches.opt_str("lldb-version").as_deref().and_then(debuggers::extract_lldb_version);

View file

@ -1,5 +1,3 @@
use rand::Rng;
use rustc_apfloat::Float;
use rustc_middle::ty;
use rustc_middle::ty::FloatTy;
@ -83,62 +81,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(val, &dest)?;
}
}
"fma" | "relaxed_fma" => {
let [a, b, c] = check_intrinsic_arg_count(args)?;
let (a, a_len) = this.project_to_simd(a)?;
let (b, b_len) = this.project_to_simd(b)?;
let (c, c_len) = this.project_to_simd(c)?;
let (dest, dest_len) = this.project_to_simd(dest)?;
assert_eq!(dest_len, a_len);
assert_eq!(dest_len, b_len);
assert_eq!(dest_len, c_len);
for i in 0..dest_len {
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)?;
let fuse: bool = intrinsic_name == "fma"
|| (this.machine.float_nondet && this.machine.rng.get_mut().random());
// Works for f32 and f64.
// FIXME: using host floats to work around https://github.com/rust-lang/miri/issues/2468.
let ty::Float(float_ty) = dest.layout.ty.kind() else {
span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name)
};
let val = match float_ty {
FloatTy::F16 => unimplemented!("f16_f128"),
FloatTy::F32 => {
let a = a.to_f32()?;
let b = b.to_f32()?;
let c = c.to_f32()?;
let res = if fuse {
a.mul_add(b, c).value
} else {
((a * b).value + c).value
};
let res = this.adjust_nan(res, &[a, b, c]);
Scalar::from(res)
}
FloatTy::F64 => {
let a = a.to_f64()?;
let b = b.to_f64()?;
let c = c.to_f64()?;
let res = if fuse {
a.mul_add(b, c).value
} else {
((a * b).value + c).value
};
let res = this.adjust_nan(res, &[a, b, c]);
Scalar::from(res)
}
FloatTy::F128 => unimplemented!("f16_f128"),
};
this.write_scalar(val, &dest)?;
}
}
"expose_provenance" => {
let [op] = check_intrinsic_arg_count(args)?;
let (op, op_len) = this.project_to_simd(op)?;

View file

@ -1347,8 +1347,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
}
#[inline(always)]
fn float_fuse_mul_add(ecx: &mut InterpCx<'tcx, Self>) -> bool {
ecx.machine.float_nondet && ecx.machine.rng.get_mut().random()
fn float_fuse_mul_add(ecx: &InterpCx<'tcx, Self>) -> bool {
ecx.machine.float_nondet && ecx.machine.rng.borrow_mut().random()
}
#[inline(always)]

View file

@ -6,18 +6,143 @@
rustc_attrs,
intrinsics,
core_intrinsics,
repr_simd
repr_simd,
f16,
f128
)]
#![allow(incomplete_features, internal_features)]
#![allow(incomplete_features, internal_features, non_camel_case_types)]
use std::fmt::{self, Debug, Formatter};
use std::intrinsics::simd as intrinsics;
use std::ptr;
use std::simd::StdFloat;
use std::simd::prelude::*;
#[repr(simd, packed)]
#[derive(Copy)]
struct PackedSimd<T, const N: usize>([T; N]);
impl<T: Copy, const N: usize> Clone for PackedSimd<T, N> {
fn clone(&self) -> Self {
*self
}
}
impl<T: PartialEq + Copy, const N: usize> PartialEq for PackedSimd<T, N> {
fn eq(&self, other: &Self) -> bool {
self.into_array() == other.into_array()
}
}
impl<T: Debug + Copy, const N: usize> Debug for PackedSimd<T, N> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.into_array(), f)
}
}
type f16x2 = PackedSimd<f16, 2>;
type f16x4 = PackedSimd<f16, 4>;
type f128x2 = PackedSimd<f128, 2>;
type f128x4 = PackedSimd<f128, 4>;
impl<T: Copy, const N: usize> PackedSimd<T, N> {
fn splat(x: T) -> Self {
Self([x; N])
}
fn from_array(a: [T; N]) -> Self {
Self(a)
}
fn into_array(self) -> [T; N] {
// as we have `repr(packed)`, there shouldn't be any padding bytes
unsafe { std::mem::transmute_copy(&self) }
}
}
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn simd_shuffle_const_generic<T, U, const IDX: &'static [u32]>(x: T, y: T) -> U;
pub fn simd_ops_f16() {
use intrinsics::*;
// small hack to make type inference better
macro_rules! assert_eq {
($a:expr, $b:expr $(,$t:tt)*) => {{
let a = $a;
let b = $b;
if false { let _inference = b == a; }
::std::assert_eq!(a, b, $(,$t)*)
}}
}
let a = f16x4::splat(10.0);
let b = f16x4::from_array([1.0, 2.0, 3.0, -4.0]);
unsafe {
assert_eq!(simd_neg(b), f16x4::from_array([-1.0, -2.0, -3.0, 4.0]));
assert_eq!(simd_add(a, b), f16x4::from_array([11.0, 12.0, 13.0, 6.0]));
assert_eq!(simd_sub(a, b), f16x4::from_array([9.0, 8.0, 7.0, 14.0]));
assert_eq!(simd_mul(a, b), f16x4::from_array([10.0, 20.0, 30.0, -40.0]));
assert_eq!(simd_div(b, a), f16x4::from_array([0.1, 0.2, 0.3, -0.4]));
assert_eq!(simd_div(a, f16x4::splat(2.0)), f16x4::splat(5.0));
assert_eq!(simd_rem(a, b), f16x4::from_array([0.0, 0.0, 1.0, 2.0]));
assert_eq!(simd_fabs(b), f16x4::from_array([1.0, 2.0, 3.0, 4.0]));
assert_eq!(
simd_fmax(a, simd_mul(b, f16x4::splat(4.0))),
f16x4::from_array([10.0, 10.0, 12.0, 10.0])
);
assert_eq!(
simd_fmin(a, simd_mul(b, f16x4::splat(4.0))),
f16x4::from_array([4.0, 8.0, 10.0, -16.0])
);
assert_eq!(simd_fma(a, b, a), simd_add(simd_mul(a, b), a));
assert_eq!(simd_fma(b, b, a), simd_add(simd_mul(b, b), a));
assert_eq!(simd_fma(a, b, b), simd_add(simd_mul(a, b), b));
assert_eq!(
simd_fma(f16x4::splat(-3.2), b, f16x4::splat(f16::NEG_INFINITY)),
f16x4::splat(f16::NEG_INFINITY)
);
assert_eq!(simd_relaxed_fma(a, b, a), simd_add(simd_mul(a, b), a));
assert_eq!(simd_relaxed_fma(b, b, a), simd_add(simd_mul(b, b), a));
assert_eq!(simd_relaxed_fma(a, b, b), simd_add(simd_mul(a, b), b));
assert_eq!(
simd_relaxed_fma(f16x4::splat(-3.2), b, f16x4::splat(f16::NEG_INFINITY)),
f16x4::splat(f16::NEG_INFINITY)
);
assert_eq!(simd_eq(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([0, !0, 0, 0]));
assert_eq!(simd_ne(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([!0, 0, !0, !0]));
assert_eq!(simd_le(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([0, !0, !0, 0]));
assert_eq!(simd_lt(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([0, 0, !0, 0]));
assert_eq!(simd_ge(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([!0, !0, 0, !0]));
assert_eq!(simd_gt(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([!0, 0, 0, !0]));
assert_eq!(simd_reduce_add_ordered(a, 0.0), 40.0f16);
assert_eq!(simd_reduce_add_ordered(b, 0.0), 2.0f16);
assert_eq!(simd_reduce_mul_ordered(a, 1.0), 10000.0f16);
assert_eq!(simd_reduce_mul_ordered(b, 1.0), -24.0f16);
assert_eq!(simd_reduce_max(a), 10.0f16);
assert_eq!(simd_reduce_max(b), 3.0f16);
assert_eq!(simd_reduce_min(a), 10.0f16);
assert_eq!(simd_reduce_min(b), -4.0f16);
assert_eq!(
simd_fmax(f16x2::from_array([0.0, f16::NAN]), f16x2::from_array([f16::NAN, 0.0])),
f16x2::from_array([0.0, 0.0])
);
assert_eq!(simd_reduce_max(f16x2::from_array([0.0, f16::NAN])), 0.0f16);
assert_eq!(simd_reduce_max(f16x2::from_array([f16::NAN, 0.0])), 0.0f16);
assert_eq!(
simd_fmin(f16x2::from_array([0.0, f16::NAN]), f16x2::from_array([f16::NAN, 0.0])),
f16x2::from_array([0.0, 0.0])
);
assert_eq!(simd_reduce_min(f16x2::from_array([0.0, f16::NAN])), 0.0f16);
assert_eq!(simd_reduce_min(f16x2::from_array([f16::NAN, 0.0])), 0.0f16);
}
}
fn simd_ops_f32() {
let a = f32x4::splat(10.0);
let b = f32x4::from_array([1.0, 2.0, 3.0, -4.0]);
@ -148,6 +273,87 @@ fn simd_ops_f64() {
assert_eq!(f64x2::from_array([f64::NAN, 0.0]).reduce_min(), 0.0);
}
pub fn simd_ops_f128() {
use intrinsics::*;
// small hack to make type inference better
macro_rules! assert_eq {
($a:expr, $b:expr $(,$t:tt)*) => {{
let a = $a;
let b = $b;
if false { let _inference = b == a; }
::std::assert_eq!(a, b, $(,$t)*)
}}
}
let a = f128x4::splat(10.0);
let b = f128x4::from_array([1.0, 2.0, 3.0, -4.0]);
unsafe {
assert_eq!(simd_neg(b), f128x4::from_array([-1.0, -2.0, -3.0, 4.0]));
assert_eq!(simd_add(a, b), f128x4::from_array([11.0, 12.0, 13.0, 6.0]));
assert_eq!(simd_sub(a, b), f128x4::from_array([9.0, 8.0, 7.0, 14.0]));
assert_eq!(simd_mul(a, b), f128x4::from_array([10.0, 20.0, 30.0, -40.0]));
assert_eq!(simd_div(b, a), f128x4::from_array([0.1, 0.2, 0.3, -0.4]));
assert_eq!(simd_div(a, f128x4::splat(2.0)), f128x4::splat(5.0));
assert_eq!(simd_rem(a, b), f128x4::from_array([0.0, 0.0, 1.0, 2.0]));
assert_eq!(simd_fabs(b), f128x4::from_array([1.0, 2.0, 3.0, 4.0]));
assert_eq!(
simd_fmax(a, simd_mul(b, f128x4::splat(4.0))),
f128x4::from_array([10.0, 10.0, 12.0, 10.0])
);
assert_eq!(
simd_fmin(a, simd_mul(b, f128x4::splat(4.0))),
f128x4::from_array([4.0, 8.0, 10.0, -16.0])
);
assert_eq!(simd_fma(a, b, a), simd_add(simd_mul(a, b), a));
assert_eq!(simd_fma(b, b, a), simd_add(simd_mul(b, b), a));
assert_eq!(simd_fma(a, b, b), simd_add(simd_mul(a, b), b));
assert_eq!(
simd_fma(f128x4::splat(-3.2), b, f128x4::splat(f128::NEG_INFINITY)),
f128x4::splat(f128::NEG_INFINITY)
);
assert_eq!(simd_relaxed_fma(a, b, a), simd_add(simd_mul(a, b), a));
assert_eq!(simd_relaxed_fma(b, b, a), simd_add(simd_mul(b, b), a));
assert_eq!(simd_relaxed_fma(a, b, b), simd_add(simd_mul(a, b), b));
assert_eq!(
simd_relaxed_fma(f128x4::splat(-3.2), b, f128x4::splat(f128::NEG_INFINITY)),
f128x4::splat(f128::NEG_INFINITY)
);
assert_eq!(simd_eq(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([0, !0, 0, 0]));
assert_eq!(simd_ne(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([!0, 0, !0, !0]));
assert_eq!(simd_le(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([0, !0, !0, 0]));
assert_eq!(simd_lt(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([0, 0, !0, 0]));
assert_eq!(simd_ge(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([!0, !0, 0, !0]));
assert_eq!(simd_gt(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([!0, 0, 0, !0]));
assert_eq!(simd_reduce_add_ordered(a, 0.0), 40.0f128);
assert_eq!(simd_reduce_add_ordered(b, 0.0), 2.0f128);
assert_eq!(simd_reduce_mul_ordered(a, 1.0), 10000.0f128);
assert_eq!(simd_reduce_mul_ordered(b, 1.0), -24.0f128);
assert_eq!(simd_reduce_max(a), 10.0f128);
assert_eq!(simd_reduce_max(b), 3.0f128);
assert_eq!(simd_reduce_min(a), 10.0f128);
assert_eq!(simd_reduce_min(b), -4.0f128);
assert_eq!(
simd_fmax(f128x2::from_array([0.0, f128::NAN]), f128x2::from_array([f128::NAN, 0.0])),
f128x2::from_array([0.0, 0.0])
);
assert_eq!(simd_reduce_max(f128x2::from_array([0.0, f128::NAN])), 0.0f128);
assert_eq!(simd_reduce_max(f128x2::from_array([f128::NAN, 0.0])), 0.0f128);
assert_eq!(
simd_fmin(f128x2::from_array([0.0, f128::NAN]), f128x2::from_array([f128::NAN, 0.0])),
f128x2::from_array([0.0, 0.0])
);
assert_eq!(simd_reduce_min(f128x2::from_array([0.0, f128::NAN])), 0.0f128);
assert_eq!(simd_reduce_min(f128x2::from_array([f128::NAN, 0.0])), 0.0f128);
}
}
fn simd_ops_i32() {
let a = i32x4::splat(10);
let b = i32x4::from_array([1, 2, 3, -4]);
@ -563,6 +769,31 @@ fn simd_gather_scatter() {
}
fn simd_round() {
unsafe {
use intrinsics::*;
assert_eq!(
simd_ceil(f16x4::from_array([0.9, 1.001, 2.0, -4.5])),
f16x4::from_array([1.0, 2.0, 2.0, -4.0])
);
assert_eq!(
simd_floor(f16x4::from_array([0.9, 1.001, 2.0, -4.5])),
f16x4::from_array([0.0, 1.0, 2.0, -5.0])
);
assert_eq!(
simd_round(f16x4::from_array([0.9, 1.001, 2.0, -4.5])),
f16x4::from_array([1.0, 1.0, 2.0, -5.0])
);
assert_eq!(
simd_round_ties_even(f16x4::from_array([0.9, 1.001, 2.0, -4.5])),
f16x4::from_array([1.0, 1.0, 2.0, -4.0])
);
assert_eq!(
simd_trunc(f16x4::from_array([0.9, 1.001, 2.0, -4.5])),
f16x4::from_array([0.0, 1.0, 2.0, -4.0])
);
}
assert_eq!(
f32x4::from_array([0.9, 1.001, 2.0, -4.5]).ceil(),
f32x4::from_array([1.0, 2.0, 2.0, -4.0])
@ -604,6 +835,31 @@ fn simd_round() {
f64x4::from_array([0.9, 1.001, 2.0, -4.5]).trunc(),
f64x4::from_array([0.0, 1.0, 2.0, -4.0])
);
unsafe {
use intrinsics::*;
assert_eq!(
simd_ceil(f128x4::from_array([0.9, 1.001, 2.0, -4.5])),
f128x4::from_array([1.0, 2.0, 2.0, -4.0])
);
assert_eq!(
simd_floor(f128x4::from_array([0.9, 1.001, 2.0, -4.5])),
f128x4::from_array([0.0, 1.0, 2.0, -5.0])
);
assert_eq!(
simd_round(f128x4::from_array([0.9, 1.001, 2.0, -4.5])),
f128x4::from_array([1.0, 1.0, 2.0, -5.0])
);
assert_eq!(
simd_round_ties_even(f128x4::from_array([0.9, 1.001, 2.0, -4.5])),
f128x4::from_array([1.0, 1.0, 2.0, -4.0])
);
assert_eq!(
simd_trunc(f128x4::from_array([0.9, 1.001, 2.0, -4.5])),
f128x4::from_array([0.0, 1.0, 2.0, -4.0])
);
}
}
fn simd_intrinsics() {
@ -724,8 +980,10 @@ fn simd_ops_non_pow2() {
fn main() {
simd_mask();
simd_ops_f16();
simd_ops_f32();
simd_ops_f64();
simd_ops_f128();
simd_ops_i32();
simd_ops_non_pow2();
simd_cast();

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//! Tests that we bail out when there are multiple assignments to the same local.
//@ test-mir-pass: CopyProp
@ -12,6 +11,14 @@ fn cond() -> bool {
// EMIT_MIR branch.foo.CopyProp.diff
fn foo() -> i32 {
// CHECK-LABEL: fn foo(
// CHECK: debug x => [[x:_.*]];
// CHECK: debug y => [[y:_.*]];
// CHECK: bb3: {
// CHECK: [[y]] = copy [[x]];
// CHECK: bb5: {
// CHECK: [[y]] = copy [[x]];
// CHECK: _0 = copy [[y]];
let x = val();
let y = if cond() {

View file

@ -1,4 +1,3 @@
// skip-filecheck
// Check that CopyProp does propagate return values of call terminators.
//@ test-mir-pass: CopyProp
//@ needs-unwind
@ -13,6 +12,13 @@ fn dummy(x: u8) -> u8 {
// EMIT_MIR calls.nrvo.CopyProp.diff
fn nrvo() -> u8 {
// CHECK-LABEL: fn nrvo(
// CHECK: debug y => _0;
// CHECK-NOT: StorageLive(_1);
// CHECK-NOT: _1 = dummy(const 5_u8)
// CHECK: _0 = dummy(const 5_u8)
// CHECK-NOT: _0 = copy _1;
// CHECK-NOT: StorageDead(_1);
let y = dummy(5); // this should get NRVO
y
}
@ -20,6 +26,11 @@ fn nrvo() -> u8 {
// EMIT_MIR calls.multiple_edges.CopyProp.diff
#[custom_mir(dialect = "runtime", phase = "initial")]
fn multiple_edges(t: bool) -> u8 {
// CHECK-LABEL: fn multiple_edges(
// CHECK: bb1: {
// CHECK: _2 = dummy(const 13_u8)
// CHECK: bb2: {
// CHECK: _0 = copy _2;
mir! {
let x: u8;
{

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
// Check that CopyProp does not propagate an assignment to a function argument
// (doing so can break usages of the original argument value)
@ -9,25 +8,46 @@ fn dummy(x: u8) -> u8 {
// EMIT_MIR copy_propagation_arg.foo.CopyProp.diff
fn foo(mut x: u8) {
// CHECK-LABEL: fn foo(
// CHECK: debug x => [[x:_.*]];
// CHECK: [[three:_.*]] = copy [[x]];
// CHECK: [[two:_.*]] = dummy(move [[three]])
// CHECK: [[x]] = move [[two]];
// calling `dummy` to make a use of `x` that copyprop cannot eliminate
x = dummy(x); // this will assign a local to `x`
}
// EMIT_MIR copy_propagation_arg.bar.CopyProp.diff
fn bar(mut x: u8) {
// CHECK-LABEL: fn bar(
// CHECK: debug x => [[x:_.*]];
// CHECK: [[three:_.*]] = copy [[x]];
// CHECK: dummy(move [[three]])
// CHECK: [[x]] = const 5_u8;
dummy(x);
x = 5;
}
// EMIT_MIR copy_propagation_arg.baz.CopyProp.diff
fn baz(mut x: i32) -> i32 {
// self-assignment to a function argument should be eliminated
// CHECK-LABEL: fn baz(
// CHECK: debug x => [[x:_.*]];
// CHECK: [[x2:_.*]] = copy [[x]];
// CHECK: [[x]] = move [[x2]];
// CHECK: _0 = copy [[x]];
// In the original case for DestProp, the self-assignment to a function argument is eliminated,
// but in CopyProp it is not eliminated.
x = x;
x
}
// EMIT_MIR copy_propagation_arg.arg_src.CopyProp.diff
fn arg_src(mut x: i32) -> i32 {
// CHECK-LABEL: fn arg_src(
// CHECK: debug x => [[x:_.*]];
// CHECK: debug y => [[y:_.*]];
// CHECK: [[y]] = copy [[x]];
// CHECK: [[x]] = const 123_i32;
let y = x;
x = 123; // Don't propagate this assignment to `y`
y

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//@ test-mir-pass: CopyProp
@ -12,6 +11,13 @@ struct NotCopy(bool);
// EMIT_MIR custom_move_arg.f.CopyProp.diff
#[custom_mir(dialect = "runtime")]
fn f(_1: NotCopy) {
// CHECK-LABEL: fn f(
// CHECK: bb0: {
// CHECK-NOT: _2 = copy _1;
// CHECK: _0 = opaque::<NotCopy>(copy _1)
// CHECK: bb1: {
// CHECK-NOT: _3 = move _2;
// CHECK: _0 = opaque::<NotCopy>(copy _1)
mir! {
{
let _2 = _1;

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//! Tests that cyclic assignments don't hang CopyProp, and result in reasonable code.
//@ test-mir-pass: CopyProp
@ -8,6 +7,18 @@ fn val() -> i32 {
// EMIT_MIR cycle.main.CopyProp.diff
fn main() {
// CHECK-LABEL: fn main(
// CHECK: debug x => [[x:_.*]];
// CHECK: debug y => [[y:_.*]];
// CHECK: debug z => [[y]];
// CHECK-NOT: StorageLive([[y]]);
// CHECK: [[y]] = copy [[x]];
// CHECK-NOT: StorageLive(_3);
// CHECK-NOT: _3 = copy [[y]];
// CHECK-NOT: StorageLive(_4);
// CHECK-NOT: _4 = copy _3;
// CHECK-NOT: _1 = move _4;
// CHECK: [[x]] = copy [[y]];
let mut x = val();
let y = x;
let z = y;

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//@ test-mir-pass: CopyProp
@ -8,6 +7,14 @@ fn id<T>(x: T) -> T {
// EMIT_MIR dead_stores_79191.f.CopyProp.after.mir
fn f(mut a: usize) -> usize {
// CHECK-LABEL: fn f(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug b => [[b:_.*]];
// CHECK: [[b]] = copy [[a]];
// CHECK: [[a]] = const 5_usize;
// CHECK: [[a]] = copy [[b]];
// CHECK: [[c:_.*]] = copy [[a]]
// CHECK: id::<usize>(move [[c]])
let b = a;
a = 5;
a = b;

View file

@ -1,26 +0,0 @@
// MIR for `f` after CopyProp
fn f(_1: usize) -> usize {
debug a => _1;
let mut _0: usize;
let _2: usize;
let mut _3: usize;
let mut _4: usize;
scope 1 {
debug b => _2;
}
bb0: {
_2 = copy _1;
_1 = const 5_usize;
_1 = copy _2;
StorageLive(_4);
_4 = copy _1;
_0 = id::<usize>(move _4) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageDead(_4);
return;
}
}

View file

@ -1,26 +0,0 @@
// MIR for `f` after CopyProp
fn f(_1: usize) -> usize {
debug a => _1;
let mut _0: usize;
let _2: usize;
let mut _3: usize;
let mut _4: usize;
scope 1 {
debug b => _2;
}
bb0: {
_2 = copy _1;
_1 = const 5_usize;
_1 = copy _2;
StorageLive(_4);
_4 = copy _1;
_0 = id::<usize>(move _4) -> [return: bb1, unwind continue];
}
bb1: {
StorageDead(_4);
return;
}
}

View file

@ -1,23 +0,0 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
// This is a copy of the `dead_stores_79191` test, except that we turn on DSE. This demonstrates
// that that pass enables this one to do more optimizations.
//@ test-mir-pass: CopyProp
//@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination
fn id<T>(x: T) -> T {
x
}
// EMIT_MIR dead_stores_better.f.CopyProp.after.mir
pub fn f(mut a: usize) -> usize {
let b = a;
a = 5;
a = b;
id(a)
}
fn main() {
f(0);
}

View file

@ -1,9 +1,12 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//@ test-mir-pass: CopyProp
// EMIT_MIR issue_107511.main.CopyProp.diff
fn main() {
// CHECK-LABEL: fn main(
// CHECK: debug i => [[i:_.*]];
// CHECK-NOT: StorageLive([[i]]);
// CHECK-NOT: StorageDead([[i]]);
let mut sum = 0;
let a = [0, 10, 20, 30];

View file

@ -1,10 +1,13 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
// Test that we do not move multiple times from the same local.
//@ test-mir-pass: CopyProp
// EMIT_MIR move_arg.f.CopyProp.diff
pub fn f<T: Copy>(a: T) {
// CHECK-LABEL: fn f(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug b => [[a]];
// CHECK: g::<T>(copy [[a]], copy [[a]])
let b = a;
g(a, b);
}

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//@ test-mir-pass: CopyProp
@ -15,6 +14,15 @@ struct Foo(u8);
#[custom_mir(dialect = "runtime")]
fn f(a: Foo) -> bool {
// CHECK-LABEL: fn f(
// CHECK-SAME: [[a:_.*]]: Foo)
// CHECK: bb0: {
// CHECK-NOT: _2 = copy [[a]];
// CHECK-NOT: _3 = move (_2.0: u8);
// CHECK: [[c:_.*]] = copy ([[a]].0: u8);
// CHECK: _0 = opaque::<Foo>(copy [[a]])
// CHECK: bb1: {
// CHECK: _0 = opaque::<u8>(move [[c]])
mir! {
{
let b = a;

View file

@ -1,4 +1,3 @@
// skip-filecheck
//@ test-mir-pass: CopyProp
//
// This attempts to mutate `a` via a pointer derived from `addr_of!(a)`. That is UB
@ -18,6 +17,10 @@ use core::intrinsics::mir::*;
#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
fn f(c: bool) -> bool {
// CHECK-LABEL: fn f(
// CHECK: _2 = copy _1;
// CHECK-NOT: _3 = &raw const _1;
// CHECK: _3 = &raw const _2;
mir! {
{
let a = c;

View file

@ -1,4 +1,3 @@
// skip-filecheck
//@ test-mir-pass: CopyProp
#![feature(custom_mir, core_intrinsics)]
@ -8,6 +7,11 @@ use core::intrinsics::mir::*;
#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
fn f(c: bool) -> bool {
// CHECK-LABEL: fn f(
// CHECK: bb2: {
// CHECK: _2 = copy _3;
// CHECK: bb3: {
// CHECK: _0 = copy _2;
mir! {
let a: bool;
let b: bool;

View file

@ -1,4 +1,3 @@
// skip-filecheck
//@ test-mir-pass: CopyProp
// Verify that we do not ICE on partial initializations.
@ -9,6 +8,9 @@ use core::intrinsics::mir::*;
// EMIT_MIR partial_init.main.CopyProp.diff
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
pub fn main() {
// CHECK-LABEL: fn main(
// CHECK: let mut [[x:_.*]]: (isize,);
// CHECK: ([[x]].0: isize) = const 1_isize;
mir! (
let x: (isize, );
{

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
// Check that CopyProp considers reborrows as not mutating the pointer.
//@ test-mir-pass: CopyProp
@ -8,6 +7,9 @@ fn opaque(_: impl Sized) {}
// EMIT_MIR reborrow.remut.CopyProp.diff
fn remut(mut x: u8) {
// CHECK-LABEL: fn remut(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug c => [[a]];
let a = &mut x;
let b = &mut *a; //< this cannot mutate a.
let c = a; //< so `c` and `a` can be merged.
@ -16,6 +18,9 @@ fn remut(mut x: u8) {
// EMIT_MIR reborrow.reraw.CopyProp.diff
fn reraw(mut x: u8) {
// CHECK-LABEL: fn reraw(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug c => [[a]];
let a = &mut x;
let b = &raw mut *a; //< this cannot mutate a.
let c = a; //< so `c` and `a` can be merged.
@ -24,6 +29,9 @@ fn reraw(mut x: u8) {
// EMIT_MIR reborrow.miraw.CopyProp.diff
fn miraw(mut x: u8) {
// CHECK-LABEL: fn miraw(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug c => [[a]];
let a = &raw mut x;
let b = unsafe { &raw mut *a }; //< this cannot mutate a.
let c = a; //< so `c` and `a` can be merged.
@ -32,6 +40,9 @@ fn miraw(mut x: u8) {
// EMIT_MIR reborrow.demiraw.CopyProp.diff
fn demiraw(mut x: u8) {
// CHECK-LABEL: fn demiraw(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug c => [[a]];
let a = &raw mut x;
let b = unsafe { &mut *a }; //< this cannot mutate a.
let c = a; //< so `c` and `a` can be merged.

View file

@ -0,0 +1,6 @@
fn main() {}
#[test]
fn a_test() {
foobar::ok();
}

View file

@ -0,0 +1,19 @@
//@ needs-target-std
use run_make_support::{assert_contains, rfs};
#[path = "../rustdoc-scrape-examples-remap/scrape.rs"]
mod scrape;
fn main() {
scrape::scrape(
&["--scrape-tests", "--emit=dep-info"],
&["--emit=dep-info,invocation-specific"],
);
let content = rfs::read_to_string("foobar.d").replace(r"\", "/");
assert_contains(&content, "lib.rs:");
assert_contains(&content, "rustdoc/ex.calls:");
let content = rfs::read_to_string("ex.d").replace(r"\", "/");
assert_contains(&content, "examples/ex.rs:");
}

View file

@ -0,0 +1,3 @@
//@ has foobar/fn.ok.html '//*[@class="docblock scraped-example-list"]' ''
pub fn ok() {}

View file

@ -3,5 +3,5 @@
mod scrape;
fn main() {
scrape::scrape(&[]);
scrape::scrape(&[], &[]);
}

View file

@ -3,5 +3,5 @@
mod scrape;
fn main() {
scrape::scrape(&[]);
scrape::scrape(&[], &[]);
}

View file

@ -3,5 +3,5 @@
mod scrape;
fn main() {
scrape::scrape(&[]);
scrape::scrape(&[], &[]);
}

View file

@ -2,5 +2,5 @@
mod scrape;
fn main() {
scrape::scrape(&[]);
scrape::scrape(&[], &[]);
}

View file

@ -2,7 +2,7 @@ use std::path::Path;
use run_make_support::{htmldocck, rfs, rustc, rustdoc};
pub fn scrape(extra_args: &[&str]) {
pub fn scrape(extra_args_scrape: &[&str], extra_args_doc: &[&str]) {
let out_dir = Path::new("rustdoc");
let crate_name = "foobar";
let deps = rfs::read_dir("examples")
@ -27,7 +27,7 @@ pub fn scrape(extra_args: &[&str]) {
.arg(&out_example)
.arg("--scrape-examples-target-crate")
.arg(crate_name)
.args(extra_args)
.args(extra_args_scrape)
.run();
out_deps.push(out_example);
}
@ -42,6 +42,7 @@ pub fn scrape(extra_args: &[&str]) {
for dep in out_deps {
rustdoc.arg("--with-examples").arg(dep);
}
rustdoc.args(extra_args_doc);
rustdoc.run();
htmldocck().arg(out_dir).arg("src/lib.rs").run();

View file

@ -3,5 +3,5 @@
mod scrape;
fn main() {
scrape::scrape(&["--scrape-tests"]);
scrape::scrape(&["--scrape-tests"], &[]);
}

View file

@ -3,5 +3,5 @@
mod scrape;
fn main() {
scrape::scrape(&[]);
scrape::scrape(&[], &[]);
}

View file

@ -25,4 +25,43 @@ fn main() {
.target("aarch64-unknown-none-softfloat")
.arg("-Zfixed-x18")
.run();
rustdoc()
.input("c.rs")
.crate_type("rlib")
.extern_("d", "libd.rmeta")
.target("aarch64-unknown-none-softfloat")
.arg("-Zfixed-x18")
.arg("--test")
.run();
rustdoc()
.input("c.rs")
.edition("2024")
.crate_type("rlib")
.extern_("d", "libd.rmeta")
.target("aarch64-unknown-none-softfloat")
.arg("-Zfixed-x18")
.arg("--test")
.run();
// rustdoc --test detects ABI mismatch
rustdoc()
.input("c.rs")
.crate_type("rlib")
.extern_("d", "libd.rmeta")
.target("aarch64-unknown-none-softfloat")
.arg("--test")
.run_fail()
.assert_stderr_contains("mixing `-Zfixed-x18` will cause an ABI mismatch");
// rustdoc --test -Cunsafe-allow-abi-mismatch=... ignores the mismatch
rustdoc()
.input("c.rs")
.crate_type("rlib")
.extern_("d", "libd.rmeta")
.target("aarch64-unknown-none-softfloat")
.arg("--test")
.arg("-Cunsafe-allow-abi-mismatch=fixed-x18")
.run();
}

View file

@ -0,0 +1,113 @@
//@ run-rustfix
#![allow(unused)]
pub const P1: *const u8 = 0 as _;
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
pub const P2: *mut u8 = 1 as _;
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
pub const P3: *const i32 = std::ptr::null();
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
pub const P4: *const i32 = std::ptr::null();
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
pub const P5: *mut i32 = std::ptr::null_mut();
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
pub const P6: *mut i32 = std::ptr::null_mut();
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
pub const P7: *const Vec<u8> = std::ptr::null();
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
pub const P8: *const std::collections::HashMap<String, i32> = std::ptr::null();
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
fn func1(p: *const u8) {}
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
fn func2(p: *mut u8) {}
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
fn func3() -> *const u8 { std::ptr::null() }
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
fn func4() -> *mut u8 { std::ptr::null_mut() }
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
struct S1 {
field: *const u8,
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
}
struct S2 {
field: *mut u8,
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
}
type Tuple1 = (*const u8, i32);
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
type Tuple2 = (*mut u8, i32);
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
type Array1 = [*const u8; 10];
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
type Array2 = [*mut u8; 10];
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
type Alias1 = *const u8;
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
type Alias2 = *mut u8;
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
pub const P9: *const u8 = std::ptr::null();
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
pub const P10: *const u8 = std::ptr::null();
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
impl S1 {
fn method(self, size: *const u32) {}
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
}
trait Trait1 {
fn method(p: *const u8);
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
}
fn generic_func<T>() -> *const T { std::ptr::null() }
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
fn main() {}

View file

@ -0,0 +1,113 @@
//@ run-rustfix
#![allow(unused)]
pub const P1: const* u8 = 0 as _;
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
pub const P2: mut* u8 = 1 as _;
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
pub const P3: const* i32 = std::ptr::null();
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
pub const P4: const* i32 = std::ptr::null();
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
pub const P5: mut* i32 = std::ptr::null_mut();
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
pub const P6: mut* i32 = std::ptr::null_mut();
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
pub const P7: const* Vec<u8> = std::ptr::null();
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
pub const P8: const* std::collections::HashMap<String, i32> = std::ptr::null();
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
fn func1(p: const* u8) {}
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
fn func2(p: mut* u8) {}
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
fn func3() -> const* u8 { std::ptr::null() }
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
fn func4() -> mut* u8 { std::ptr::null_mut() }
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
struct S1 {
field: const* u8,
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
}
struct S2 {
field: mut* u8,
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
}
type Tuple1 = (const* u8, i32);
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
type Tuple2 = (mut* u8, i32);
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
type Array1 = [const* u8; 10];
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
type Array2 = [mut* u8; 10];
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
type Alias1 = const* u8;
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
type Alias2 = mut* u8;
//~^ ERROR: raw pointer types must be written as `*mut T`
//~| HELP: put the `*` before `mut`
pub const P9: const *u8 = std::ptr::null();
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
pub const P10: const * u8 = std::ptr::null();
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
impl S1 {
fn method(self, size: const* u32) {}
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
}
trait Trait1 {
fn method(p: const* u8);
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
}
fn generic_func<T>() -> const* T { std::ptr::null() }
//~^ ERROR: raw pointer types must be written as `*const T`
//~| HELP: put the `*` before `const`
fn main() {}

View file

@ -0,0 +1,302 @@
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:5:15
|
LL | pub const P1: const* u8 = 0 as _;
| ^^^^^
|
help: put the `*` before `const`
|
LL - pub const P1: const* u8 = 0 as _;
LL + pub const P1: *const u8 = 0 as _;
|
error: raw pointer types must be written as `*mut T`
--> $DIR/c-style-pointer-types.rs:9:15
|
LL | pub const P2: mut* u8 = 1 as _;
| ^^^
|
help: put the `*` before `mut`
|
LL - pub const P2: mut* u8 = 1 as _;
LL + pub const P2: *mut u8 = 1 as _;
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:13:15
|
LL | pub const P3: const* i32 = std::ptr::null();
| ^^^^^
|
help: put the `*` before `const`
|
LL - pub const P3: const* i32 = std::ptr::null();
LL + pub const P3: *const i32 = std::ptr::null();
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:17:15
|
LL | pub const P4: const* i32 = std::ptr::null();
| ^^^^^
|
help: put the `*` before `const`
|
LL - pub const P4: const* i32 = std::ptr::null();
LL + pub const P4: *const i32 = std::ptr::null();
|
error: raw pointer types must be written as `*mut T`
--> $DIR/c-style-pointer-types.rs:21:15
|
LL | pub const P5: mut* i32 = std::ptr::null_mut();
| ^^^
|
help: put the `*` before `mut`
|
LL - pub const P5: mut* i32 = std::ptr::null_mut();
LL + pub const P5: *mut i32 = std::ptr::null_mut();
|
error: raw pointer types must be written as `*mut T`
--> $DIR/c-style-pointer-types.rs:25:15
|
LL | pub const P6: mut* i32 = std::ptr::null_mut();
| ^^^
|
help: put the `*` before `mut`
|
LL - pub const P6: mut* i32 = std::ptr::null_mut();
LL + pub const P6: *mut i32 = std::ptr::null_mut();
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:29:15
|
LL | pub const P7: const* Vec<u8> = std::ptr::null();
| ^^^^^
|
help: put the `*` before `const`
|
LL - pub const P7: const* Vec<u8> = std::ptr::null();
LL + pub const P7: *const Vec<u8> = std::ptr::null();
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:33:15
|
LL | pub const P8: const* std::collections::HashMap<String, i32> = std::ptr::null();
| ^^^^^
|
help: put the `*` before `const`
|
LL - pub const P8: const* std::collections::HashMap<String, i32> = std::ptr::null();
LL + pub const P8: *const std::collections::HashMap<String, i32> = std::ptr::null();
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:37:13
|
LL | fn func1(p: const* u8) {}
| ^^^^^
|
help: put the `*` before `const`
|
LL - fn func1(p: const* u8) {}
LL + fn func1(p: *const u8) {}
|
error: raw pointer types must be written as `*mut T`
--> $DIR/c-style-pointer-types.rs:41:13
|
LL | fn func2(p: mut* u8) {}
| ^^^
|
help: put the `*` before `mut`
|
LL - fn func2(p: mut* u8) {}
LL + fn func2(p: *mut u8) {}
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:45:15
|
LL | fn func3() -> const* u8 { std::ptr::null() }
| ^^^^^
|
help: put the `*` before `const`
|
LL - fn func3() -> const* u8 { std::ptr::null() }
LL + fn func3() -> *const u8 { std::ptr::null() }
|
error: raw pointer types must be written as `*mut T`
--> $DIR/c-style-pointer-types.rs:49:15
|
LL | fn func4() -> mut* u8 { std::ptr::null_mut() }
| ^^^
|
help: put the `*` before `mut`
|
LL - fn func4() -> mut* u8 { std::ptr::null_mut() }
LL + fn func4() -> *mut u8 { std::ptr::null_mut() }
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:54:12
|
LL | field: const* u8,
| ^^^^^
|
help: put the `*` before `const`
|
LL - field: const* u8,
LL + field: *const u8,
|
error: raw pointer types must be written as `*mut T`
--> $DIR/c-style-pointer-types.rs:60:12
|
LL | field: mut* u8,
| ^^^
|
help: put the `*` before `mut`
|
LL - field: mut* u8,
LL + field: *mut u8,
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:65:16
|
LL | type Tuple1 = (const* u8, i32);
| ^^^^^
|
help: put the `*` before `const`
|
LL - type Tuple1 = (const* u8, i32);
LL + type Tuple1 = (*const u8, i32);
|
error: raw pointer types must be written as `*mut T`
--> $DIR/c-style-pointer-types.rs:69:16
|
LL | type Tuple2 = (mut* u8, i32);
| ^^^
|
help: put the `*` before `mut`
|
LL - type Tuple2 = (mut* u8, i32);
LL + type Tuple2 = (*mut u8, i32);
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:73:16
|
LL | type Array1 = [const* u8; 10];
| ^^^^^
|
help: put the `*` before `const`
|
LL - type Array1 = [const* u8; 10];
LL + type Array1 = [*const u8; 10];
|
error: raw pointer types must be written as `*mut T`
--> $DIR/c-style-pointer-types.rs:77:16
|
LL | type Array2 = [mut* u8; 10];
| ^^^
|
help: put the `*` before `mut`
|
LL - type Array2 = [mut* u8; 10];
LL + type Array2 = [*mut u8; 10];
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:81:15
|
LL | type Alias1 = const* u8;
| ^^^^^
|
help: put the `*` before `const`
|
LL - type Alias1 = const* u8;
LL + type Alias1 = *const u8;
|
error: raw pointer types must be written as `*mut T`
--> $DIR/c-style-pointer-types.rs:85:15
|
LL | type Alias2 = mut* u8;
| ^^^
|
help: put the `*` before `mut`
|
LL - type Alias2 = mut* u8;
LL + type Alias2 = *mut u8;
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:89:15
|
LL | pub const P9: const *u8 = std::ptr::null();
| ^^^^^
|
help: put the `*` before `const`
|
LL - pub const P9: const *u8 = std::ptr::null();
LL + pub const P9: *const u8 = std::ptr::null();
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:93:16
|
LL | pub const P10: const * u8 = std::ptr::null();
| ^^^^^
|
help: put the `*` before `const`
|
LL - pub const P10: const * u8 = std::ptr::null();
LL + pub const P10: *const u8 = std::ptr::null();
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:98:27
|
LL | fn method(self, size: const* u32) {}
| ^^^^^
|
help: put the `*` before `const`
|
LL - fn method(self, size: const* u32) {}
LL + fn method(self, size: *const u32) {}
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:104:18
|
LL | fn method(p: const* u8);
| ^^^^^
|
help: put the `*` before `const`
|
LL - fn method(p: const* u8);
LL + fn method(p: *const u8);
|
error: raw pointer types must be written as `*const T`
--> $DIR/c-style-pointer-types.rs:109:25
|
LL | fn generic_func<T>() -> const* T { std::ptr::null() }
| ^^^^^
|
help: put the `*` before `const`
|
LL - fn generic_func<T>() -> const* T { std::ptr::null() }
LL + fn generic_func<T>() -> *const T { std::ptr::null() }
|
error: aborting due to 25 previous errors