Auto merge of #131690 - matthiaskrgr:rollup-mcau4ol, r=matthiaskrgr

Rollup of 8 pull requests

Successful merges:

 - #129424 (Stabilize `Pin::as_deref_mut()`)
 - #131332 (Fix clobber_abi and disallow SVE-related registers in Arm64EC inline assembly)
 - #131384 (Update precondition tests (especially for zero-size access to null))
 - #131430 (Special treatment empty tuple when suggest adding a string literal in format macro.)
 - #131550 (Make some tweaks to extern block diagnostics)
 - #131667 (Fix AArch64InlineAsmReg::emit)
 - #131679 (compiletest: Document various parts of compiletest's `lib.rs`)
 - #131682 (Tag PRs affecting compiletest with `A-compiletest`)

Failed merges:

 - #131496 (Stabilise `const_make_ascii`.)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-10-14 16:38:40 +00:00
commit 9322d183f4
69 changed files with 977 additions and 200 deletions

View File

@ -62,12 +62,12 @@ ast_passes_equality_in_where = equality constraints are not yet supported in `wh
ast_passes_extern_block_suggestion = if you meant to declare an externally defined function, use an `extern` block
ast_passes_extern_fn_qualifiers = functions in `extern` blocks cannot have qualifiers
ast_passes_extern_fn_qualifiers = functions in `extern` blocks cannot have `{$kw}` qualifier
.label = in this `extern` block
.suggestion = remove this qualifier
.suggestion = remove the `{$kw}` qualifier
ast_passes_extern_invalid_safety = items in unadorned `extern` blocks cannot have safety qualifiers
.suggestion = add unsafe to this `extern` block
ast_passes_extern_invalid_safety = items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
.suggestion = add `unsafe` to this `extern` block
ast_passes_extern_item_ascii = items in `extern` blocks cannot use non-ascii identifiers
.label = in this `extern` block

View File

@ -524,21 +524,24 @@ impl<'a> AstValidator<'a> {
// Deconstruct to ensure exhaustiveness
FnHeader { safety: _, coroutine_kind, constness, ext }: FnHeader,
) {
let report_err = |span| {
self.dcx()
.emit_err(errors::FnQualifierInExtern { span, block: self.current_extern_span() });
let report_err = |span, kw| {
self.dcx().emit_err(errors::FnQualifierInExtern {
span,
kw,
block: self.current_extern_span(),
});
};
match coroutine_kind {
Some(knd) => report_err(knd.span()),
Some(kind) => report_err(kind.span(), kind.as_str()),
None => (),
}
match constness {
Const::Yes(span) => report_err(span),
Const::Yes(span) => report_err(span, "const"),
Const::No => (),
}
match ext {
Extern::None => (),
Extern::Implicit(span) | Extern::Explicit(_, span) => report_err(span),
Extern::Implicit(span) | Extern::Explicit(_, span) => report_err(span, "extern"),
}
}

View File

@ -295,6 +295,7 @@ pub(crate) struct FnQualifierInExtern {
pub span: Span,
#[label]
pub block: Span,
pub kw: &'static str,
}
#[derive(Diagnostic)]

View File

@ -195,12 +195,26 @@ fn make_format_args(
Applicability::MaybeIncorrect,
);
} else {
let sugg_fmt = match args.explicit_args().len() {
0 => "{}".to_string(),
count => {
format!("{}{{}}", "{} ".repeat(count))
// `{}` or `()`
let should_suggest = |kind: &ExprKind| -> bool {
match kind {
ExprKind::Block(b, None) if b.stmts.is_empty() => true,
ExprKind::Tup(v) if v.is_empty() => true,
_ => false,
}
};
let mut sugg_fmt = String::new();
for kind in std::iter::once(&efmt.kind)
.chain(args.explicit_args().into_iter().map(|a| &a.expr.kind))
{
sugg_fmt.push_str(if should_suggest(kind) {
"{:?} "
} else {
"{} "
});
}
sugg_fmt = sugg_fmt.trim_end().to_string();
err.span_suggestion(
unexpanded_fmt_span.shrink_to_lo(),
"you might be missing a string literal to format with",

View File

@ -542,57 +542,16 @@ fn xmm_reg_index(reg: InlineAsmReg) -> Option<u32> {
/// If the register is an AArch64 integer register then return its index.
fn a64_reg_index(reg: InlineAsmReg) -> Option<u32> {
use AArch64InlineAsmReg::*;
// Unlike `a64_vreg_index`, we can't subtract `x0` to get the u32 because
// `x19` and `x29` are missing and the integer constants for the
// `x0`..`x30` enum variants don't all match the register number. E.g. the
// integer constant for `x18` is 18, but the constant for `x20` is 19.
Some(match reg {
InlineAsmReg::AArch64(r) => match r {
x0 => 0,
x1 => 1,
x2 => 2,
x3 => 3,
x4 => 4,
x5 => 5,
x6 => 6,
x7 => 7,
x8 => 8,
x9 => 9,
x10 => 10,
x11 => 11,
x12 => 12,
x13 => 13,
x14 => 14,
x15 => 15,
x16 => 16,
x17 => 17,
x18 => 18,
// x19 is reserved
x20 => 20,
x21 => 21,
x22 => 22,
x23 => 23,
x24 => 24,
x25 => 25,
x26 => 26,
x27 => 27,
x28 => 28,
// x29 is reserved
x30 => 30,
_ => return None,
},
_ => return None,
})
match reg {
InlineAsmReg::AArch64(r) => r.reg_index(),
_ => None,
}
}
/// If the register is an AArch64 vector register then return its index.
fn a64_vreg_index(reg: InlineAsmReg) -> Option<u32> {
use AArch64InlineAsmReg::*;
match reg {
InlineAsmReg::AArch64(reg) if reg as u32 >= v0 as u32 && reg as u32 <= v31 as u32 => {
Some(reg as u32 - v0 as u32)
}
InlineAsmReg::AArch64(reg) => reg.vreg_index(),
_ => None,
}
}

View File

@ -64,6 +64,7 @@ impl AArch64InlineAsmRegClass {
neon: I8, I16, I32, I64, F16, F32, F64, F128,
VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF16(4), VecF32(2), VecF64(1),
VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2);
// Note: When adding support for SVE vector types, they must be rejected for Arm64EC.
},
Self::preg => &[],
}
@ -96,7 +97,7 @@ fn restricted_for_arm64ec(
_is_clobber: bool,
) -> Result<(), &'static str> {
if arch == InlineAsmArch::Arm64EC {
Err("x13, x14, x23, x24, x28, v16-v31 cannot be used for Arm64EC")
Err("x13, x14, x23, x24, x28, v16-v31, p*, ffr cannot be used for Arm64EC")
} else {
Ok(())
}
@ -165,23 +166,23 @@ def_regs! {
v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29", "z29"] % restricted_for_arm64ec,
v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30", "z30"] % restricted_for_arm64ec,
v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31", "z31"] % restricted_for_arm64ec,
p0: preg = ["p0"],
p1: preg = ["p1"],
p2: preg = ["p2"],
p3: preg = ["p3"],
p4: preg = ["p4"],
p5: preg = ["p5"],
p6: preg = ["p6"],
p7: preg = ["p7"],
p8: preg = ["p8"],
p9: preg = ["p9"],
p10: preg = ["p10"],
p11: preg = ["p11"],
p12: preg = ["p12"],
p13: preg = ["p13"],
p14: preg = ["p14"],
p15: preg = ["p15"],
ffr: preg = ["ffr"],
p0: preg = ["p0"] % restricted_for_arm64ec,
p1: preg = ["p1"] % restricted_for_arm64ec,
p2: preg = ["p2"] % restricted_for_arm64ec,
p3: preg = ["p3"] % restricted_for_arm64ec,
p4: preg = ["p4"] % restricted_for_arm64ec,
p5: preg = ["p5"] % restricted_for_arm64ec,
p6: preg = ["p6"] % restricted_for_arm64ec,
p7: preg = ["p7"] % restricted_for_arm64ec,
p8: preg = ["p8"] % restricted_for_arm64ec,
p9: preg = ["p9"] % restricted_for_arm64ec,
p10: preg = ["p10"] % restricted_for_arm64ec,
p11: preg = ["p11"] % restricted_for_arm64ec,
p12: preg = ["p12"] % restricted_for_arm64ec,
p13: preg = ["p13"] % restricted_for_arm64ec,
p14: preg = ["p14"] % restricted_for_arm64ec,
p15: preg = ["p15"] % restricted_for_arm64ec,
ffr: preg = ["ffr"] % restricted_for_arm64ec,
#error = ["x19", "w19"] =>
"x19 is used internally by LLVM and cannot be used as an operand for inline asm",
#error = ["x29", "w29", "fp", "wfp"] =>
@ -200,12 +201,66 @@ impl AArch64InlineAsmReg {
_arch: InlineAsmArch,
modifier: Option<char>,
) -> fmt::Result {
let (prefix, index) = if (self as u32) < Self::v0 as u32 {
(modifier.unwrap_or('x'), self as u32 - Self::x0 as u32)
let (prefix, index) = if let Some(index) = self.reg_index() {
(modifier.unwrap_or('x'), index)
} else if let Some(index) = self.vreg_index() {
(modifier.unwrap_or('v'), index)
} else {
(modifier.unwrap_or('v'), self as u32 - Self::v0 as u32)
return out.write_str(self.name());
};
assert!(index < 32);
write!(out, "{prefix}{index}")
}
/// If the register is an integer register then return its index.
pub fn reg_index(self) -> Option<u32> {
// Unlike `vreg_index`, we can't subtract `x0` to get the u32 because
// `x19` and `x29` are missing and the integer constants for the
// `x0`..`x30` enum variants don't all match the register number. E.g. the
// integer constant for `x18` is 18, but the constant for `x20` is 19.
use AArch64InlineAsmReg::*;
Some(match self {
x0 => 0,
x1 => 1,
x2 => 2,
x3 => 3,
x4 => 4,
x5 => 5,
x6 => 6,
x7 => 7,
x8 => 8,
x9 => 9,
x10 => 10,
x11 => 11,
x12 => 12,
x13 => 13,
x14 => 14,
x15 => 15,
x16 => 16,
x17 => 17,
x18 => 18,
// x19 is reserved
x20 => 20,
x21 => 21,
x22 => 22,
x23 => 23,
x24 => 24,
x25 => 25,
x26 => 26,
x27 => 27,
x28 => 28,
// x29 is reserved
x30 => 30,
_ => return None,
})
}
/// If the register is a vector register then return its index.
pub fn vreg_index(self) -> Option<u32> {
use AArch64InlineAsmReg::*;
if self as u32 >= v0 as u32 && self as u32 <= v31 as u32 {
return Some(self as u32 - v0 as u32);
}
None
}
}

View File

@ -890,6 +890,7 @@ pub enum InlineAsmClobberAbi {
Arm,
AArch64,
AArch64NoX18,
Arm64EC,
RiscV,
LoongArch,
S390x,
@ -932,7 +933,7 @@ impl InlineAsmClobberAbi {
_ => Err(&["C", "system", "efiapi"]),
},
InlineAsmArch::Arm64EC => match name {
"C" | "system" => Ok(InlineAsmClobberAbi::AArch64NoX18),
"C" | "system" => Ok(InlineAsmClobberAbi::Arm64EC),
_ => Err(&["C", "system"]),
},
InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name {
@ -1033,7 +1034,6 @@ impl InlineAsmClobberAbi {
p0, p1, p2, p3, p4, p5, p6, p7,
p8, p9, p10, p11, p12, p13, p14, p15,
ffr,
}
},
InlineAsmClobberAbi::AArch64NoX18 => clobbered_regs! {
@ -1052,7 +1052,20 @@ impl InlineAsmClobberAbi {
p0, p1, p2, p3, p4, p5, p6, p7,
p8, p9, p10, p11, p12, p13, p14, p15,
ffr,
}
},
InlineAsmClobberAbi::Arm64EC => clobbered_regs! {
AArch64 AArch64InlineAsmReg {
// x13 and x14 cannot be used in Arm64EC.
x0, x1, x2, x3, x4, x5, x6, x7,
x8, x9, x10, x11, x12, x15,
x16, x17, x30,
// Technically the low 64 bits of v8-v15 are preserved, but
// we have no way of expressing this using clobbers.
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10, v11, v12, v13, v14, v15,
// v16-v31, p*, and ffr cannot be used in Arm64EC.
}
},
InlineAsmClobberAbi::Arm => clobbered_regs! {

View File

@ -506,7 +506,7 @@ impl AsciiChar {
pub const unsafe fn digit_unchecked(d: u8) -> Self {
assert_unsafe_precondition!(
check_language_ub,
"`AsciiChar::digit_unchecked` input cannot exceed 9.",
"`ascii::Char::digit_unchecked` input cannot exceed 9.",
(d: u8 = d) => d < 10
);

View File

@ -64,6 +64,7 @@
#![allow(missing_docs)]
use crate::marker::{DiscriminantKind, Tuple};
use crate::mem::SizedTypeProperties;
use crate::{ptr, ub_checks};
pub mod mir;
@ -3364,10 +3365,12 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
size: usize = size_of::<T>(),
align: usize = align_of::<T>(),
count: usize = count,
) =>
ub_checks::is_aligned_and_not_null(src, align)
&& ub_checks::is_aligned_and_not_null(dst, align)
&& ub_checks::is_nonoverlapping(src, dst, size, count)
) => {
let zero_size = count == 0 || size == 0;
ub_checks::is_aligned_and_not_null(src, align, zero_size)
&& ub_checks::is_aligned_and_not_null(dst, align, zero_size)
&& ub_checks::is_nonoverlapping(src, dst, size, count)
}
);
// SAFETY: the safety contract for `copy_nonoverlapping` must be
@ -3465,9 +3468,10 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
src: *const () = src as *const (),
dst: *mut () = dst as *mut (),
align: usize = align_of::<T>(),
zero_size: bool = T::IS_ZST || count == 0,
) =>
ub_checks::is_aligned_and_not_null(src, align)
&& ub_checks::is_aligned_and_not_null(dst, align)
ub_checks::is_aligned_and_not_null(src, align, zero_size)
&& ub_checks::is_aligned_and_not_null(dst, align, zero_size)
);
copy(src, dst, count)
}
@ -3544,7 +3548,8 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
(
addr: *const () = dst as *const (),
align: usize = align_of::<T>(),
) => ub_checks::is_aligned_and_not_null(addr, align)
zero_size: bool = T::IS_ZST || count == 0,
) => ub_checks::is_aligned_and_not_null(addr, align, zero_size)
);
write_bytes(dst, val, count)
}

View File

@ -1422,7 +1422,7 @@ impl<Ptr: DerefMut> Pin<Ptr> {
/// move in the future, and this method does not enable the pointee to move. "Malicious"
/// implementations of `Ptr::DerefMut` are likewise ruled out by the contract of
/// `Pin::new_unchecked`.
#[unstable(feature = "pin_deref_mut", issue = "86918")]
#[stable(feature = "pin_deref_mut", since = "CURRENT_RUSTC_VERSION")]
#[must_use = "`self` will be dropped if the result is not used"]
#[inline(always)]
pub fn as_deref_mut(self: Pin<&mut Pin<Ptr>>) -> Pin<&mut Ptr::Target> {

View File

@ -448,7 +448,7 @@
use crate::cmp::Ordering;
use crate::marker::FnPtr;
use crate::mem::{self, MaybeUninit};
use crate::mem::{self, MaybeUninit, SizedTypeProperties};
use crate::{fmt, hash, intrinsics, ub_checks};
mod alignment;
@ -1165,10 +1165,12 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
size: usize = size_of::<T>(),
align: usize = align_of::<T>(),
count: usize = count,
) =>
ub_checks::is_aligned_and_not_null(x, align)
&& ub_checks::is_aligned_and_not_null(y, align)
&& ub_checks::is_nonoverlapping(x, y, size, count)
) => {
let zero_size = size == 0 || count == 0;
ub_checks::is_aligned_and_not_null(x, align, zero_size)
&& ub_checks::is_aligned_and_not_null(y, align, zero_size)
&& ub_checks::is_nonoverlapping(x, y, size, count)
}
);
// Split up the slice into small power-of-two-sized chunks that LLVM is able
@ -1278,7 +1280,8 @@ pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T {
(
addr: *const () = dst as *const (),
align: usize = align_of::<T>(),
) => ub_checks::is_aligned_and_not_null(addr, align)
is_zst: bool = T::IS_ZST,
) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
);
mem::replace(&mut *dst, src)
}
@ -1430,7 +1433,8 @@ pub const unsafe fn read<T>(src: *const T) -> T {
(
addr: *const () = src as *const (),
align: usize = align_of::<T>(),
) => ub_checks::is_aligned_and_not_null(addr, align)
is_zst: bool = T::IS_ZST,
) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
);
crate::intrinsics::read_via_copy(src)
}
@ -1635,7 +1639,8 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) {
(
addr: *mut () = dst as *mut (),
align: usize = align_of::<T>(),
) => ub_checks::is_aligned_and_not_null(addr, align)
is_zst: bool = T::IS_ZST,
) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
);
intrinsics::write_via_move(dst, src)
}
@ -1808,7 +1813,8 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
(
addr: *const () = src as *const (),
align: usize = align_of::<T>(),
) => ub_checks::is_aligned_and_not_null(addr, align)
is_zst: bool = T::IS_ZST,
) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
);
intrinsics::volatile_load(src)
}
@ -1887,7 +1893,8 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
(
addr: *mut () = dst as *mut (),
align: usize = align_of::<T>(),
) => ub_checks::is_aligned_and_not_null(addr, align)
is_zst: bool = T::IS_ZST,
) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
);
intrinsics::volatile_store(dst, src);
}

View File

@ -132,7 +132,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
align: usize = align_of::<T>(),
len: usize = len,
) =>
ub_checks::is_aligned_and_not_null(data, align)
ub_checks::is_aligned_and_not_null(data, align, false)
&& ub_checks::is_valid_allocation_size(size, len)
);
&*ptr::slice_from_raw_parts(data, len)
@ -187,7 +187,7 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m
align: usize = align_of::<T>(),
len: usize = len,
) =>
ub_checks::is_aligned_and_not_null(data, align)
ub_checks::is_aligned_and_not_null(data, align, false)
&& ub_checks::is_valid_allocation_size(size, len)
);
&mut *ptr::slice_from_raw_parts_mut(data, len)

View File

@ -109,15 +109,15 @@ pub(crate) const fn check_language_ub() -> bool {
intrinsics::ub_checks() && const_eval_select((), comptime, runtime)
}
/// Checks whether `ptr` is properly aligned with respect to
/// `align_of::<T>()`.
/// Checks whether `ptr` is properly aligned with respect to the given alignment, and
/// if `is_zst == false`, that `ptr` is not null.
///
/// In `const` this is approximate and can fail spuriously. It is primarily intended
/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the
/// check is anyway not executed in `const`.
#[inline]
pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool {
!ptr.is_null() && ptr.is_aligned_to(align)
pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize, is_zst: bool) -> bool {
ptr.is_aligned_to(align) && (is_zst || !ptr.is_null())
}
#[inline]

View File

@ -43,6 +43,11 @@ use crate::common::{
use crate::header::HeadersCache;
use crate::util::logv;
/// Creates the `Config` instance for this invocation of compiletest.
///
/// The config mostly reflects command-line arguments, but there might also be
/// some code here that inspects environment variables or even runs executables
/// (e.g. when discovering debugger versions).
pub fn parse_config(args: Vec<String>) -> Config {
let mut opts = Options::new();
opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH")
@ -413,6 +418,7 @@ pub fn opt_str2(maybestr: Option<String>) -> String {
}
}
/// Called by `main` after the config has been parsed.
pub fn run_tests(config: Arc<Config>) {
// If we want to collect rustfix coverage information,
// we first make sure that the coverage file does not exist.
@ -454,6 +460,8 @@ pub fn run_tests(config: Arc<Config>) {
configs.push(config.clone());
};
// Discover all of the tests in the test suite directory, and build a libtest
// structure for each test (or each revision of a multi-revision test).
let mut tests = Vec::new();
for c in configs {
let mut found_paths = HashSet::new();
@ -463,7 +471,12 @@ pub fn run_tests(config: Arc<Config>) {
tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice()));
// Delegate to libtest to filter and run the big list of structures created
// during test discovery. When libtest decides to run a test, it will invoke
// the corresponding closure created by `make_test_closure`.
let res = test::run_tests_console(&opts, tests);
// Check the outcome reported by libtest.
match res {
Ok(true) => {}
Ok(false) => {
@ -532,6 +545,11 @@ pub fn test_opts(config: &Config) -> test::TestOpts {
}
}
/// Creates libtest structures for every test/revision in the test suite directory.
///
/// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests),
/// regardless of whether any filters/tests were specified on the command-line,
/// because filtering is handled later by libtest.
pub fn make_tests(
config: Arc<Config>,
tests: &mut Vec<test::TestDescAndFn>,
@ -610,10 +628,17 @@ fn common_inputs_stamp(config: &Config) -> Stamp {
stamp
}
/// Returns a list of modified/untracked test files that should be run when
/// the `--only-modified` flag is in use.
///
/// (Might be inaccurate in some cases.)
fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> {
// If `--only-modified` wasn't passed, the list of modified tests won't be
// used for anything, so avoid some work and just return an empty list.
if !config.only_modified {
return Ok(vec![]);
}
let files =
get_git_modified_files(&config.git_config(), Some(dir), &vec!["rs", "stderr", "fixed"])?
.unwrap_or(vec![]);
@ -634,6 +659,8 @@ fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> {
Ok(full_paths)
}
/// Recursively scans a directory to find test files and create test structures
/// that will be handed over to libtest.
fn collect_tests_from_dir(
config: Arc<Config>,
cache: &HeadersCache,
@ -650,6 +677,8 @@ fn collect_tests_from_dir(
return Ok(());
}
// For run-make tests, a "test file" is actually a directory that contains
// an `rmake.rs` or `Makefile`"
if config.mode == Mode::RunMake {
if dir.join("Makefile").exists() && dir.join("rmake.rs").exists() {
return Err(io::Error::other(
@ -663,6 +692,7 @@ fn collect_tests_from_dir(
relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
};
tests.extend(make_test(config, cache, &paths, inputs, poisoned));
// This directory is a test, so don't try to find other tests inside it.
return Ok(());
}
}
@ -677,22 +707,27 @@ fn collect_tests_from_dir(
fs::create_dir_all(&build_dir).unwrap();
// Add each `.rs` file as a test, and recurse further on any
// subdirectories we find, except for `aux` directories.
// subdirectories we find, except for `auxiliary` directories.
// FIXME: this walks full tests tree, even if we have something to ignore
// use walkdir/ignore like in tidy?
for file in fs::read_dir(dir)? {
let file = file?;
let file_path = file.path();
let file_name = file.file_name();
if is_test(&file_name) && (!config.only_modified || modified_tests.contains(&file_path)) {
// We found a test file, so create the corresponding libtest structures.
debug!("found test file: {:?}", file_path.display());
// Record the stem of the test file, to check for overlaps later.
let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap());
found_paths.insert(rel_test_path);
let paths =
TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
tests.extend(make_test(config.clone(), cache, &paths, inputs, poisoned))
} else if file_path.is_dir() {
// Recurse to find more tests in a subdirectory.
let relative_file_path = relative_dir_path.join(file.file_name());
if &file_name != "auxiliary" {
debug!("found directory: {:?}", file_path.display());
@ -728,6 +763,8 @@ pub fn is_test(file_name: &OsString) -> bool {
!invalid_prefixes.iter().any(|p| file_name.starts_with(p))
}
/// For a single test file, creates one or more test structures (one per revision)
/// that can be handed over to libtest to run, possibly in parallel.
fn make_test(
config: Arc<Config>,
cache: &HeadersCache,
@ -735,6 +772,9 @@ fn make_test(
inputs: &Stamp,
poisoned: &mut bool,
) -> Vec<test::TestDescAndFn> {
// For run-make tests, each "test file" is actually a _directory_ containing
// an `rmake.rs` or `Makefile`. But for the purposes of directive parsing,
// we want to look at that recipe file, not the directory itself.
let test_path = if config.mode == Mode::RunMake {
if testpaths.file.join("rmake.rs").exists() && testpaths.file.join("Makefile").exists() {
panic!("run-make tests cannot have both `rmake.rs` and `Makefile`");
@ -750,26 +790,40 @@ fn make_test(
} else {
PathBuf::from(&testpaths.file)
};
// Scan the test file to discover its revisions, if any.
let early_props = EarlyProps::from_file(&config, &test_path);
// Incremental tests are special, they inherently cannot be run in parallel.
// `runtest::run` will be responsible for iterating over revisions.
// Normally we create one libtest structure per revision, with two exceptions:
// - If a test doesn't use revisions, create a dummy revision (None) so that
// the test can still run.
// - Incremental tests inherently can't run their revisions in parallel, so
// we treat them like non-revisioned tests here. Incremental revisions are
// handled internally by `runtest::run` instead.
let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
vec![None]
} else {
early_props.revisions.iter().map(|r| Some(r.as_str())).collect()
};
// For each revision (or the sole dummy revision), create and return a
// `test::TestDescAndFn` that can be handed over to libtest.
revisions
.into_iter()
.map(|revision| {
// Create a test name and description to hand over to libtest.
let src_file =
std::fs::File::open(&test_path).expect("open test file to parse ignores");
let test_name = crate::make_test_name(&config, testpaths, revision);
// Create a libtest description for the test/revision.
// This is where `ignore-*`/`only-*`/`needs-*` directives are handled,
// because they need to set the libtest ignored flag.
let mut desc = make_test_description(
&config, cache, test_name, &test_path, src_file, revision, poisoned,
);
// Ignore tests that already run and are up to date with respect to inputs.
// If a test's inputs haven't changed since the last time it ran,
// mark it as ignored so that libtest will skip it.
if !config.force_rerun
&& is_up_to_date(&config, testpaths, &early_props, revision, inputs)
{
@ -777,18 +831,25 @@ fn make_test(
// Keep this in sync with the "up-to-date" message detected by bootstrap.
desc.ignore_message = Some("up-to-date");
}
test::TestDescAndFn {
desc,
testfn: make_test_closure(config.clone(), testpaths, revision),
}
// Create the callback that will run this test/revision when libtest calls it.
let testfn = make_test_closure(config.clone(), testpaths, revision);
test::TestDescAndFn { desc, testfn }
})
.collect()
}
/// The path of the `stamp` file that gets created or updated whenever a
/// particular test completes successfully.
fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
output_base_dir(config, testpaths, revision).join("stamp")
}
/// Returns a list of files that, if modified, would cause this test to no
/// longer be up-to-date.
///
/// (Might be inaccurate in some cases.)
fn files_related_to_test(
config: &Config,
testpaths: &TestPaths,
@ -824,46 +885,61 @@ fn files_related_to_test(
related
}
/// Checks whether a particular test/revision is "up-to-date", meaning that no
/// relevant files/settings have changed since the last time the test succeeded.
///
/// (This is not very reliable in some circumstances, so the `--force-rerun`
/// flag can be used to ignore up-to-date checking and always re-run tests.)
fn is_up_to_date(
config: &Config,
testpaths: &TestPaths,
props: &EarlyProps,
revision: Option<&str>,
inputs: &Stamp,
inputs: &Stamp, // Last-modified timestamp of the compiler, compiletest etc
) -> bool {
let stamp_name = stamp(config, testpaths, revision);
// Check hash.
// Check the config hash inside the stamp file.
let contents = match fs::read_to_string(&stamp_name) {
Ok(f) => f,
Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
// The test hasn't succeeded yet, so it is not up-to-date.
Err(_) => return false,
};
let expected_hash = runtest::compute_stamp_hash(config);
if contents != expected_hash {
// Some part of compiletest configuration has changed since the test
// last succeeded, so it is not up-to-date.
return false;
}
// Check timestamps.
// Check the timestamp of the stamp file against the last modified time
// of all files known to be relevant to the test.
let mut inputs = inputs.clone();
for path in files_related_to_test(config, testpaths, props, revision) {
inputs.add_path(&path);
}
// If no relevant files have been modified since the stamp file was last
// written, the test is up-to-date.
inputs < Stamp::from_path(&stamp_name)
}
/// The maximum of a set of file-modified timestamps.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Stamp {
time: SystemTime,
}
impl Stamp {
/// Creates a timestamp holding the last-modified time of the specified file.
fn from_path(path: &Path) -> Self {
let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH };
stamp.add_path(path);
stamp
}
/// Updates this timestamp to the last-modified time of the specified file,
/// if it is later than the currently-stored timestamp.
fn add_path(&mut self, path: &Path) {
let modified = fs::metadata(path)
.and_then(|metadata| metadata.modified())
@ -871,6 +947,9 @@ impl Stamp {
self.time = self.time.max(modified);
}
/// Updates this timestamp to the most recent last-modified time of all files
/// recursively contained in the given directory, if it is later than the
/// currently-stored timestamp.
fn add_dir(&mut self, path: &Path) {
for entry in WalkDir::new(path) {
let entry = entry.unwrap();
@ -886,6 +965,7 @@ impl Stamp {
}
}
/// Creates a name for this test/revision that can be handed over to libtest.
fn make_test_name(
config: &Config,
testpaths: &TestPaths,
@ -914,20 +994,41 @@ fn make_test_name(
))
}
/// Creates a callback for this test/revision that libtest will call when it
/// decides to actually run the underlying test.
fn make_test_closure(
config: Arc<Config>,
testpaths: &TestPaths,
revision: Option<&str>,
) -> test::TestFn {
let config = config.clone();
let testpaths = testpaths.clone();
let revision = revision.map(str::to_owned);
// This callback is the link between compiletest's test discovery code,
// and the parts of compiletest that know how to run an individual test.
test::DynTestFn(Box::new(move || {
runtest::run(config, &testpaths, revision.as_deref());
Ok(())
}))
}
/// Checks that test discovery didn't find any tests whose name stem is a prefix
/// of some other tests's name.
///
/// For example, suppose the test suite contains these two test files:
/// - `tests/rustdoc/primitive.rs`
/// - `tests/rustdoc/primitive/no_std.rs`
///
/// The test runner might put the output from those tests in these directories:
/// - `$build/test/rustdoc/primitive/`
/// - `$build/test/rustdoc/primitive/no_std/`
///
/// Because one output path is a subdirectory of the other, the two tests might
/// interfere with each other in unwanted ways, especially if the test runner
/// decides to delete test output directories to clean them between runs.
/// To avoid problems, we forbid test names from overlapping in this way.
///
/// See <https://github.com/rust-lang/rust/pull/109509> for more context.
fn check_overlapping_tests(found_paths: &HashSet<PathBuf>) {
let mut collisions = Vec::new();
for path in found_paths {

View File

@ -0,0 +1,36 @@
//@ assembly-output: emit-asm
//@ compile-flags: --target arm64ec-pc-windows-msvc
//@ needs-llvm-components: aarch64
#![crate_type = "rlib"]
#![feature(no_core, rustc_attrs, lang_items, asm_experimental_arch)]
#![no_core]
#[lang = "sized"]
trait Sized {}
#[rustc_builtin_macro]
macro_rules! asm {
() => {};
}
// CHECK-LABEL: @cc_clobber
// CHECK: call void asm sideeffect "", "~{cc}"()
#[no_mangle]
pub unsafe fn cc_clobber() {
asm!("", options(nostack, nomem));
}
// CHECK-LABEL: @no_clobber
// CHECK: call void asm sideeffect "", ""()
#[no_mangle]
pub unsafe fn no_clobber() {
asm!("", options(nostack, nomem, preserves_flags));
}
// CHECK-LABEL: @clobber_abi
// CHECK: asm sideeffect "", "={w0},={w1},={w2},={w3},={w4},={w5},={w6},={w7},={w8},={w9},={w10},={w11},={w12},={w15},={w16},={w17},={w30},={q0},={q1},={q2},={q3},={q4},={q5},={q6},={q7},={q8},={q9},={q10},={q11},={q12},={q13},={q14},={q15}"()
#[no_mangle]
pub unsafe fn clobber_abi() {
asm!("", clobber_abi("C"), options(nostack, nomem, preserves_flags));
}

View File

@ -2,7 +2,7 @@ extern "C" {
//@ is "$.index[*][?(@.name=='f1')].inner.function.header.is_unsafe" true
pub fn f1();
// items in unadorned `extern` blocks cannot have safety qualifiers
// items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
}
unsafe extern "C" {

View File

@ -0,0 +1,28 @@
//@ only-aarch64
//@ build-pass
//@ needs-asm-support
#![crate_type = "rlib"]
#![feature(no_core, rustc_attrs, lang_items)]
#![no_core]
// AArch64 test corresponding to arm64ec-sve.rs.
#[lang = "sized"]
trait Sized {}
#[lang = "copy"]
trait Copy {}
impl Copy for f64 {}
#[rustc_builtin_macro]
macro_rules! asm {
() => {};
}
fn f(x: f64) {
unsafe {
asm!("", out("p0") _);
asm!("", out("ffr") _);
}
}

View File

@ -0,0 +1,31 @@
//@ compile-flags: --target arm64ec-pc-windows-msvc
//@ needs-asm-support
//@ needs-llvm-components: aarch64
#![crate_type = "rlib"]
#![feature(no_core, rustc_attrs, lang_items, asm_experimental_arch)]
#![no_core]
// SVE cannot be used for Arm64EC
// https://github.com/rust-lang/rust/pull/131332#issuecomment-2401189142
#[lang = "sized"]
trait Sized {}
#[lang = "copy"]
trait Copy {}
impl Copy for f64 {}
#[rustc_builtin_macro]
macro_rules! asm {
() => {};
}
fn f(x: f64) {
unsafe {
asm!("", out("p0") _);
//~^ ERROR cannot use register `p0`
asm!("", out("ffr") _);
//~^ ERROR cannot use register `ffr`
}
}

View File

@ -0,0 +1,14 @@
error: cannot use register `p0`: x13, x14, x23, x24, x28, v16-v31, p*, ffr cannot be used for Arm64EC
--> $DIR/arm64ec-sve.rs:26:18
|
LL | asm!("", out("p0") _);
| ^^^^^^^^^^^
error: cannot use register `ffr`: x13, x14, x23, x24, x28, v16-v31, p*, ffr cannot be used for Arm64EC
--> $DIR/arm64ec-sve.rs:28:18
|
LL | asm!("", out("ffr") _);
| ^^^^^^^^^^^^
error: aborting due to 2 previous errors

View File

@ -2,7 +2,7 @@
extern {
async fn L() { //~ ERROR: incorrect function inside `extern` block
//~^ ERROR: functions in `extern` blocks cannot have qualifiers
//~^ ERROR: functions in `extern` blocks cannot have `async` qualifier
async fn M() {}
}
}

View File

@ -15,13 +15,13 @@ LL | | }
= help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block
= note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html
error: functions in `extern` blocks cannot have qualifiers
error: functions in `extern` blocks cannot have `async` qualifier
--> $DIR/issue-95829.rs:4:5
|
LL | extern {
| ------ in this `extern` block
LL | async fn L() {
| ^^^^^ help: remove this qualifier
| ^^^^^ help: remove the `async` qualifier
error: aborting due to 2 previous errors

View File

@ -0,0 +1,13 @@
//@ run-rustfix
fn main() {
let s = "123";
println!("{:?} {} {}", {}, "sss", s);
//~^ ERROR format argument must be a string literal
println!("{:?}", {});
//~^ ERROR format argument must be a string literal
println!("{} {} {} {:?}", s, "sss", s, {});
//~^ ERROR format argument must be a string literal
println!("{:?} {} {:?}", (), s, {});
//~^ ERROR format argument must be a string literal
}

View File

@ -0,0 +1,13 @@
//@ run-rustfix
fn main() {
let s = "123";
println!({}, "sss", s);
//~^ ERROR format argument must be a string literal
println!({});
//~^ ERROR format argument must be a string literal
println!(s, "sss", s, {});
//~^ ERROR format argument must be a string literal
println!((), s, {});
//~^ ERROR format argument must be a string literal
}

View File

@ -0,0 +1,46 @@
error: format argument must be a string literal
--> $DIR/format-empty-block-unit-tuple-suggestion-130170.rs:5:14
|
LL | println!({}, "sss", s);
| ^^
|
help: you might be missing a string literal to format with
|
LL | println!("{:?} {} {}", {}, "sss", s);
| +++++++++++++
error: format argument must be a string literal
--> $DIR/format-empty-block-unit-tuple-suggestion-130170.rs:7:14
|
LL | println!({});
| ^^
|
help: you might be missing a string literal to format with
|
LL | println!("{:?}", {});
| +++++++
error: format argument must be a string literal
--> $DIR/format-empty-block-unit-tuple-suggestion-130170.rs:9:14
|
LL | println!(s, "sss", s, {});
| ^
|
help: you might be missing a string literal to format with
|
LL | println!("{} {} {} {:?}", s, "sss", s, {});
| ++++++++++++++++
error: format argument must be a string literal
--> $DIR/format-empty-block-unit-tuple-suggestion-130170.rs:11:14
|
LL | println!((), s, {});
| ^^
|
help: you might be missing a string literal to format with
|
LL | println!("{:?} {} {:?}", (), s, {});
| +++++++++++++++
error: aborting due to 4 previous errors

View File

@ -41,15 +41,15 @@ fn main() {
}
extern "C" {
async fn fe1(); //~ ERROR functions in `extern` blocks cannot have qualifiers
unsafe fn fe2(); //~ ERROR items in unadorned `extern` blocks cannot have safety qualifiers
const fn fe3(); //~ ERROR functions in `extern` blocks cannot have qualifiers
extern "C" fn fe4(); //~ ERROR functions in `extern` blocks cannot have qualifiers
async fn fe1(); //~ ERROR functions in `extern` blocks cannot
unsafe fn fe2(); //~ ERROR items in `extern` blocks without an `unsafe` qualifier cannot
const fn fe3(); //~ ERROR functions in `extern` blocks cannot
extern "C" fn fe4(); //~ ERROR functions in `extern` blocks cannot
const async unsafe extern "C" fn fe5();
//~^ ERROR functions in `extern` blocks
//~| ERROR functions in `extern` blocks
//~| ERROR functions in `extern` blocks
//~| ERROR functions cannot be both `const` and `async`
//~| ERROR items in unadorned `extern` blocks cannot have safety qualifiers
//~| ERROR items in `extern` blocks without an `unsafe` qualifier cannot have
}
}

View File

@ -70,77 +70,77 @@ LL | const async unsafe extern "C" fn fi5() {}
| | `async` because of this
| `const` because of this
error: functions in `extern` blocks cannot have qualifiers
error: functions in `extern` blocks cannot have `async` qualifier
--> $DIR/fn-header-semantic-fail.rs:44:9
|
LL | extern "C" {
| ---------- in this `extern` block
LL | async fn fe1();
| ^^^^^ help: remove this qualifier
| ^^^^^ help: remove the `async` qualifier
error: items in unadorned `extern` blocks cannot have safety qualifiers
error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
--> $DIR/fn-header-semantic-fail.rs:45:9
|
LL | unsafe fn fe2();
| ^^^^^^^^^^^^^^^^
|
help: add unsafe to this `extern` block
help: add `unsafe` to this `extern` block
|
LL | unsafe extern "C" {
| ++++++
error: functions in `extern` blocks cannot have qualifiers
error: functions in `extern` blocks cannot have `const` qualifier
--> $DIR/fn-header-semantic-fail.rs:46:9
|
LL | extern "C" {
| ---------- in this `extern` block
...
LL | const fn fe3();
| ^^^^^ help: remove this qualifier
| ^^^^^ help: remove the `const` qualifier
error: functions in `extern` blocks cannot have qualifiers
error: functions in `extern` blocks cannot have `extern` qualifier
--> $DIR/fn-header-semantic-fail.rs:47:9
|
LL | extern "C" {
| ---------- in this `extern` block
...
LL | extern "C" fn fe4();
| ^^^^^^^^^^ help: remove this qualifier
| ^^^^^^^^^^ help: remove the `extern` qualifier
error: functions in `extern` blocks cannot have qualifiers
error: functions in `extern` blocks cannot have `async` qualifier
--> $DIR/fn-header-semantic-fail.rs:48:15
|
LL | extern "C" {
| ---------- in this `extern` block
...
LL | const async unsafe extern "C" fn fe5();
| ^^^^^ help: remove this qualifier
| ^^^^^ help: remove the `async` qualifier
error: functions in `extern` blocks cannot have qualifiers
error: functions in `extern` blocks cannot have `const` qualifier
--> $DIR/fn-header-semantic-fail.rs:48:9
|
LL | extern "C" {
| ---------- in this `extern` block
...
LL | const async unsafe extern "C" fn fe5();
| ^^^^^ help: remove this qualifier
| ^^^^^ help: remove the `const` qualifier
error: functions in `extern` blocks cannot have qualifiers
error: functions in `extern` blocks cannot have `extern` qualifier
--> $DIR/fn-header-semantic-fail.rs:48:28
|
LL | extern "C" {
| ---------- in this `extern` block
...
LL | const async unsafe extern "C" fn fe5();
| ^^^^^^^^^^ help: remove this qualifier
| ^^^^^^^^^^ help: remove the `extern` qualifier
error: items in unadorned `extern` blocks cannot have safety qualifiers
error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
--> $DIR/fn-header-semantic-fail.rs:48:9
|
LL | const async unsafe extern "C" fn fe5();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: add unsafe to this `extern` block
help: add `unsafe` to this `extern` block
|
LL | unsafe extern "C" {
| ++++++

View File

@ -1,9 +1,9 @@
extern "C" {
const fn foo();
//~^ ERROR functions in `extern` blocks cannot have qualifiers
//~^ ERROR functions in `extern` blocks cannot
const unsafe fn bar();
//~^ ERROR functions in `extern` blocks cannot have qualifiers
//~| ERROR items in unadorned `extern` blocks cannot have safety qualifiers
//~^ ERROR functions in `extern` blocks cannot
//~| ERROR items in `extern` blocks without an `unsafe` qualifier cannot
}
fn main() {}

View File

@ -1,27 +1,27 @@
error: functions in `extern` blocks cannot have qualifiers
error: functions in `extern` blocks cannot have `const` qualifier
--> $DIR/no-const-fn-in-extern-block.rs:2:5
|
LL | extern "C" {
| ---------- in this `extern` block
LL | const fn foo();
| ^^^^^ help: remove this qualifier
| ^^^^^ help: remove the `const` qualifier
error: functions in `extern` blocks cannot have qualifiers
error: functions in `extern` blocks cannot have `const` qualifier
--> $DIR/no-const-fn-in-extern-block.rs:4:5
|
LL | extern "C" {
| ---------- in this `extern` block
...
LL | const unsafe fn bar();
| ^^^^^ help: remove this qualifier
| ^^^^^ help: remove the `const` qualifier
error: items in unadorned `extern` blocks cannot have safety qualifiers
error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
--> $DIR/no-const-fn-in-extern-block.rs:4:5
|
LL | const unsafe fn bar();
| ^^^^^^^^^^^^^^^^^^^^^^
|
help: add unsafe to this `extern` block
help: add `unsafe` to this `extern` block
|
LL | unsafe extern "C" {
| ++++++

View File

@ -0,0 +1,11 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: Alignment::new_unchecked requires
#![feature(ptr_alignment_type)]
fn main() {
unsafe {
std::ptr::Alignment::new_unchecked(0);
}
}

View File

@ -0,0 +1,11 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: `ascii::Char::digit_unchecked` input cannot exceed 9
#![feature(ascii_char)]
fn main() {
unsafe {
std::ascii::Char::digit_unchecked(b'a');
}
}

View File

@ -0,0 +1,9 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: hint::assert_unchecked must never be called when the condition is false
fn main() {
unsafe {
std::hint::assert_unchecked(false);
}
}

View File

@ -0,0 +1,9 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: invalid value for `char`
fn main() {
unsafe {
char::from_u32_unchecked(0xD801);
}
}

View File

@ -0,0 +1,25 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: ptr::copy_nonoverlapping requires
//@ revisions: null_src null_dst misaligned_src misaligned_dst overlapping
use std::ptr;
fn main() {
let src = [0u16; 3];
let mut dst = [0u16; 3];
let src = src.as_ptr();
let dst = dst.as_mut_ptr();
unsafe {
#[cfg(null_src)]
ptr::copy_nonoverlapping(ptr::null(), dst, 1);
#[cfg(null_dst)]
ptr::copy_nonoverlapping(src, ptr::null_mut(), 1);
#[cfg(misaligned_src)]
ptr::copy_nonoverlapping(src.byte_add(1), dst, 1);
#[cfg(misaligned_dst)]
ptr::copy_nonoverlapping(src, dst.byte_add(1), 1);
#[cfg(overlapping)]
ptr::copy_nonoverlapping(dst, dst.add(1), 2);
}
}

View File

@ -0,0 +1,23 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: ptr::copy requires
//@ revisions: null_src null_dst misaligned_src misaligned_dst
use std::ptr;
fn main() {
let src = [0u16; 3];
let mut dst = [0u16; 3];
let src = src.as_ptr();
let dst = dst.as_mut_ptr();
unsafe {
#[cfg(null_src)]
ptr::copy(ptr::null(), dst, 1);
#[cfg(null_dst)]
ptr::copy(src, ptr::null_mut(), 1);
#[cfg(misaligned_src)]
ptr::copy(src.byte_add(1), dst, 1);
#[cfg(misaligned_dst)]
ptr::copy(src, dst.byte_add(1), 1);
}
}

View File

@ -0,0 +1,15 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: Layout::from_size_align_unchecked requires
//@ revisions: toolarge badalign
//@[toolarge] compile-flags: --cfg toolarge
//@[badalign] compile-flags: --cfg badalign
fn main() {
unsafe {
#[cfg(toolarge)]
std::alloc::Layout::from_size_align_unchecked(isize::MAX as usize, 2);
#[cfg(badalign)]
std::alloc::Layout::from_size_align_unchecked(1, 3);
}
}

View File

@ -1,10 +0,0 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts
//@ ignore-debug
fn main() {
unsafe {
let _s: &[u64] = std::slice::from_raw_parts(1usize as *const u64, 0);
}
}

View File

@ -0,0 +1,9 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: NonNull::new_unchecked requires
fn main() {
unsafe {
std::ptr::NonNull::new_unchecked(std::ptr::null_mut::<u8>());
}
}

View File

@ -0,0 +1,12 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: NonZero::from_mut_unchecked requires
#![feature(nonzero_from_mut)]
fn main() {
unsafe {
let mut num = 0u8;
std::num::NonZeroU8::from_mut_unchecked(&mut num);
}
}

View File

@ -0,0 +1,9 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: NonZero::new_unchecked requires
fn main() {
unsafe {
std::num::NonZeroU8::new_unchecked(0);
}
}

View File

@ -1,10 +0,0 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts
//@ ignore-debug
fn main() {
unsafe {
let _s: &[u8] = std::slice::from_raw_parts(std::ptr::null(), 0);
}
}

View File

@ -1,11 +0,0 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: slice::get_unchecked requires
//@ ignore-debug
fn main() {
unsafe {
let sli: &[u8] = &[0];
sli.get_unchecked(1);
}
}

View File

@ -0,0 +1,18 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: ptr::read requires
//@ revisions: null misaligned
//@ ignore-test
use std::ptr;
fn main() {
let src = [0u16; 2];
let src = src.as_ptr();
unsafe {
#[cfg(null)]
ptr::read(ptr::null::<u8>());
#[cfg(misaligned)]
ptr::read(src.byte_add(1));
}
}

View File

@ -0,0 +1,17 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: ptr::read_volatile requires
//@ revisions: null misaligned
use std::ptr;
fn main() {
let src = [0u16; 2];
let src = src.as_ptr();
unsafe {
#[cfg(null)]
ptr::read_volatile(ptr::null::<u8>());
#[cfg(misaligned)]
ptr::read_volatile(src.byte_add(1));
}
}

View File

@ -0,0 +1,17 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: ptr::replace requires
//@ revisions: null misaligned
use std::ptr;
fn main() {
let mut dst = [0u16; 2];
let dst = dst.as_mut_ptr();
unsafe {
#[cfg(null)]
ptr::replace(ptr::null_mut::<u8>(), 1);
#[cfg(misaligned)]
ptr::replace(dst.byte_add(1), 1u16);
}
}

View File

@ -0,0 +1,16 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts_mut requires
//@ revisions: null misaligned toolarge
fn main() {
unsafe {
#[cfg(null)]
let _s: &mut [u8] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0);
#[cfg(misaligned)]
let _s: &mut [u16] = std::slice::from_raw_parts_mut(1usize as *mut u16, 0);
#[cfg(toolarge)]
let _s: &mut [u16] =
std::slice::from_raw_parts_mut(2usize as *mut u16, isize::MAX as usize);
}
}

View File

@ -0,0 +1,15 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts requires
//@ revisions: null misaligned toolarge
fn main() {
unsafe {
#[cfg(null)]
let _s: &[u8] = std::slice::from_raw_parts(std::ptr::null(), 0);
#[cfg(misaligned)]
let _s: &[u16] = std::slice::from_raw_parts(1usize as *const u16, 0);
#[cfg(toolarge)]
let _s: &[u16] = std::slice::from_raw_parts(2usize as *const u16, isize::MAX as usize);
}
}

View File

@ -0,0 +1,20 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: slice::get_unchecked requires
//@ revisions: usize range range_to range_from backwards_range
fn main() {
unsafe {
let s = &[0];
#[cfg(usize)]
s.get_unchecked(1);
#[cfg(range)]
s.get_unchecked(1..2);
#[cfg(range_to)]
s.get_unchecked(..2);
#[cfg(range_from)]
s.get_unchecked(2..);
#[cfg(backwards_range)]
s.get_unchecked(1..0);
}
}

View File

@ -0,0 +1,20 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: slice::get_unchecked_mut requires
//@ revisions: usize range range_to range_from backwards_range
fn main() {
unsafe {
let mut s = &mut [0];
#[cfg(usize)]
s.get_unchecked_mut(1);
#[cfg(range)]
s.get_unchecked_mut(1..2);
#[cfg(range_to)]
s.get_unchecked_mut(..2);
#[cfg(range_from)]
s.get_unchecked_mut(2..);
#[cfg(backwards_range)]
s.get_unchecked_mut(1..0);
}
}

View File

@ -0,0 +1,14 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: index out of bounds: the len is 2 but the index is 2
//@ revisions: oob_a oob_b
fn main() {
let mut pair = [0u8; 2];
unsafe {
#[cfg(oob_a)]
pair.swap(0, 2);
#[cfg(oob_b)]
pair.swap(2, 0);
}
}

View File

@ -0,0 +1,18 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: str::get_unchecked requires
//@ revisions: range range_to range_from backwards_range
fn main() {
unsafe {
let s = "💅";
#[cfg(range)]
s.get_unchecked(4..5);
#[cfg(range_to)]
s.get_unchecked(..5);
#[cfg(range_from)]
s.get_unchecked(5..);
#[cfg(backwards_range)]
s.get_unchecked(1..0);
}
}

View File

@ -0,0 +1,19 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: str::get_unchecked_mut requires
//@ revisions: range range_to range_from backwards_range
fn main() {
unsafe {
let mut s: String = "💅".chars().collect();
let mut s: &mut str = &mut s;
#[cfg(range)]
s.get_unchecked_mut(4..5);
#[cfg(range_to)]
s.get_unchecked_mut(..5);
#[cfg(range_from)]
s.get_unchecked_mut(5..);
#[cfg(backwards_range)]
s.get_unchecked_mut(1..0);
}
}

View File

@ -0,0 +1,25 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: ptr::swap_nonoverlapping requires
//@ revisions: null_src null_dst misaligned_src misaligned_dst overlapping
use std::ptr;
fn main() {
let mut src = [0u16; 3];
let mut dst = [0u16; 3];
let src = src.as_mut_ptr();
let dst = dst.as_mut_ptr();
unsafe {
#[cfg(null_src)]
ptr::swap_nonoverlapping(ptr::null_mut(), dst, 1);
#[cfg(null_dst)]
ptr::swap_nonoverlapping(src, ptr::null_mut(), 1);
#[cfg(misaligned_src)]
ptr::swap_nonoverlapping(src.byte_add(1), dst, 1);
#[cfg(misaligned_dst)]
ptr::swap_nonoverlapping(src, dst.byte_add(1), 1);
#[cfg(overlapping)]
ptr::swap_nonoverlapping(dst, dst.add(1), 2);
}
}

View File

@ -0,0 +1,9 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_add cannot overflow
fn main() {
unsafe {
1u8.unchecked_add(u8::MAX);
}
}

View File

@ -0,0 +1,9 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_add cannot overflow
fn main() {
unsafe {
2u8.unchecked_add(u8::MAX);
}
}

View File

@ -0,0 +1,11 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_shl cannot overflow
#![feature(unchecked_shifts)]
fn main() {
unsafe {
0u8.unchecked_shl(u8::BITS);
}
}

View File

@ -0,0 +1,11 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_shr cannot overflow
#![feature(unchecked_shifts)]
fn main() {
unsafe {
0u8.unchecked_shr(u8::BITS);
}
}

View File

@ -0,0 +1,9 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_sub cannot overflow
fn main() {
unsafe {
0u8.unchecked_sub(1u8);
}
}

View File

@ -0,0 +1,9 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached
fn main() {
unsafe {
std::hint::unreachable_unchecked();
}
}

View File

@ -0,0 +1,18 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: ptr::write requires
//@ revisions: null misaligned
//@ ignore-test
use std::ptr;
fn main() {
let mut dst = [0u16; 2];
let mut dst = dst.as_mut_ptr();
unsafe {
#[cfg(null)]
ptr::write(ptr::null_mut::<u8>(), 1u8);
#[cfg(misaligned)]
ptr::write(dst.byte_add(1), 1u16);
}
}

View File

@ -0,0 +1,18 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: ptr::write requires
//@ revisions: null misaligned
//@ ignore-test
use std::ptr;
fn main() {
let mut dst = [0u16; 2];
let mut dst = dst.as_mut_ptr();
unsafe {
#[cfg(null)]
ptr::write_bytes(ptr::null_mut::<u8>(), 1u8, 2);
#[cfg(misaligned)]
ptr::write_bytes(dst.byte_add(1), 1u8, 2);
}
}

View File

@ -0,0 +1,17 @@
//@ run-fail
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: ptr::write_volatile requires
//@ revisions: null misaligned
use std::ptr;
fn main() {
let mut dst = [0u16; 2];
let mut dst = dst.as_mut_ptr();
unsafe {
#[cfg(null)]
ptr::write_volatile(ptr::null_mut::<u8>(), 1u8);
#[cfg(misaligned)]
ptr::write_volatile(dst.byte_add(1), 1u16);
}
}

View File

@ -0,0 +1,21 @@
// Test that none of the precondition checks panic on zero-sized reads or writes through null.
//@ run-pass
//@ compile-flags: -Zmir-opt-level=0 -Copt-level=0 -Cdebug-assertions=yes
use std::ptr;
fn main() {
unsafe {
ptr::copy_nonoverlapping::<u8>(ptr::null(), ptr::null_mut(), 0);
ptr::copy_nonoverlapping::<()>(ptr::null(), ptr::null_mut(), 123);
ptr::copy::<u8>(ptr::null(), ptr::null_mut(), 0);
ptr::copy::<()>(ptr::null(), ptr::null_mut(), 123);
ptr::swap::<()>(ptr::null_mut(), ptr::null_mut());
ptr::replace::<()>(ptr::null_mut(), ());
ptr::read::<()>(ptr::null());
ptr::write::<()>(ptr::null_mut(), ());
ptr::read_volatile::<()>(ptr::null());
ptr::write_volatile::<()>(ptr::null_mut(), ());
}
}

View File

@ -1,21 +1,21 @@
error: items in unadorned `extern` blocks cannot have safety qualifiers
error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
--> $DIR/safe-unsafe-on-unadorned-extern-block.rs:8:5
|
LL | safe static TEST1: i32;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
help: add unsafe to this `extern` block
help: add `unsafe` to this `extern` block
|
LL | unsafe extern "C" {
| ++++++
error: items in unadorned `extern` blocks cannot have safety qualifiers
error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
--> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5
|
LL | safe fn test1(i: i32);
| ^^^^^^^^^^^^^^^^^^^^^^
|
help: add unsafe to this `extern` block
help: add `unsafe` to this `extern` block
|
LL | unsafe extern "C" {
| ++++++

View File

@ -10,24 +10,24 @@ LL | |
LL | | }
| |_^
error: items in unadorned `extern` blocks cannot have safety qualifiers
error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
--> $DIR/safe-unsafe-on-unadorned-extern-block.rs:8:5
|
LL | safe static TEST1: i32;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
help: add unsafe to this `extern` block
help: add `unsafe` to this `extern` block
|
LL | unsafe extern "C" {
| ++++++
error: items in unadorned `extern` blocks cannot have safety qualifiers
error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
--> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5
|
LL | safe fn test1(i: i32);
| ^^^^^^^^^^^^^^^^^^^^^^
|
help: add unsafe to this `extern` block
help: add `unsafe` to this `extern` block
|
LL | unsafe extern "C" {
| ++++++

View File

@ -6,9 +6,9 @@
extern "C" {
//[edition2024]~^ ERROR extern blocks must be unsafe
safe static TEST1: i32;
//~^ ERROR items in unadorned `extern` blocks cannot have safety qualifiers
//~^ ERROR items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
safe fn test1(i: i32);
//~^ ERROR items in unadorned `extern` blocks cannot have safety qualifiers
//~^ ERROR items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
}
fn test2() {

View File

@ -3,7 +3,7 @@
#![allow(dead_code)]
unsafe extern "C" {
unsafe fn foo(); //~ ERROR items in unadorned `extern` blocks cannot have safety qualifiers
unsafe fn foo(); //~ ERROR items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
}
fn main() {}

View File

@ -3,7 +3,7 @@
#![allow(dead_code)]
extern "C" {
unsafe fn foo(); //~ ERROR items in unadorned `extern` blocks cannot have safety qualifiers
unsafe fn foo(); //~ ERROR items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
}
fn main() {}

View File

@ -1,10 +1,10 @@
error: items in unadorned `extern` blocks cannot have safety qualifiers
error: items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
--> $DIR/unsafe-on-extern-block-issue-126756.rs:6:5
|
LL | unsafe fn foo();
| ^^^^^^^^^^^^^^^^
|
help: add unsafe to this `extern` block
help: add `unsafe` to this `extern` block
|
LL | unsafe extern "C" {
| ++++++

View File

@ -431,6 +431,11 @@ trigger_files = [
"src/tools/run-make-support"
]
[autolabel."A-compiletest"]
trigger_files = [
"src/tools/compiletest"
]
[notify-zulip."I-prioritize"]
zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts
topic = "#{number} {title}"