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_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 .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 ast_passes_extern_invalid_safety = items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
.suggestion = add unsafe to this `extern` block .suggestion = add `unsafe` to this `extern` block
ast_passes_extern_item_ascii = items in `extern` blocks cannot use non-ascii identifiers ast_passes_extern_item_ascii = items in `extern` blocks cannot use non-ascii identifiers
.label = in this `extern` block .label = in this `extern` block

View file

@ -524,21 +524,24 @@ impl<'a> AstValidator<'a> {
// Deconstruct to ensure exhaustiveness // Deconstruct to ensure exhaustiveness
FnHeader { safety: _, coroutine_kind, constness, ext }: FnHeader, FnHeader { safety: _, coroutine_kind, constness, ext }: FnHeader,
) { ) {
let report_err = |span| { let report_err = |span, kw| {
self.dcx() self.dcx().emit_err(errors::FnQualifierInExtern {
.emit_err(errors::FnQualifierInExtern { span, block: self.current_extern_span() }); span,
kw,
block: self.current_extern_span(),
});
}; };
match coroutine_kind { match coroutine_kind {
Some(knd) => report_err(knd.span()), Some(kind) => report_err(kind.span(), kind.as_str()),
None => (), None => (),
} }
match constness { match constness {
Const::Yes(span) => report_err(span), Const::Yes(span) => report_err(span, "const"),
Const::No => (), Const::No => (),
} }
match ext { match ext {
Extern::None => (), 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, pub span: Span,
#[label] #[label]
pub block: Span, pub block: Span,
pub kw: &'static str,
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]

View file

@ -195,12 +195,26 @@ fn make_format_args(
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} else { } else {
let sugg_fmt = match args.explicit_args().len() { // `{}` or `()`
0 => "{}".to_string(), let should_suggest = |kind: &ExprKind| -> bool {
count => { match kind {
format!("{}{{}}", "{} ".repeat(count)) 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( err.span_suggestion(
unexpanded_fmt_span.shrink_to_lo(), unexpanded_fmt_span.shrink_to_lo(),
"you might be missing a string literal to format with", "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. /// If the register is an AArch64 integer register then return its index.
fn a64_reg_index(reg: InlineAsmReg) -> Option<u32> { fn a64_reg_index(reg: InlineAsmReg) -> Option<u32> {
use AArch64InlineAsmReg::*; match reg {
// Unlike `a64_vreg_index`, we can't subtract `x0` to get the u32 because InlineAsmReg::AArch64(r) => r.reg_index(),
// `x19` and `x29` are missing and the integer constants for the _ => None,
// `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,
})
} }
/// If the register is an AArch64 vector register then return its index. /// If the register is an AArch64 vector register then return its index.
fn a64_vreg_index(reg: InlineAsmReg) -> Option<u32> { fn a64_vreg_index(reg: InlineAsmReg) -> Option<u32> {
use AArch64InlineAsmReg::*;
match reg { match reg {
InlineAsmReg::AArch64(reg) if reg as u32 >= v0 as u32 && reg as u32 <= v31 as u32 => { InlineAsmReg::AArch64(reg) => reg.vreg_index(),
Some(reg as u32 - v0 as u32)
}
_ => None, _ => None,
} }
} }

View file

@ -64,6 +64,7 @@ impl AArch64InlineAsmRegClass {
neon: I8, I16, I32, I64, F16, F32, F64, F128, neon: I8, I16, I32, I64, F16, F32, F64, F128,
VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF16(4), VecF32(2), VecF64(1), 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); 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 => &[], Self::preg => &[],
} }
@ -96,7 +97,7 @@ fn restricted_for_arm64ec(
_is_clobber: bool, _is_clobber: bool,
) -> Result<(), &'static str> { ) -> Result<(), &'static str> {
if arch == InlineAsmArch::Arm64EC { 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 { } else {
Ok(()) Ok(())
} }
@ -165,23 +166,23 @@ def_regs! {
v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29", "z29"] % restricted_for_arm64ec, v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29", "z29"] % restricted_for_arm64ec,
v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30", "z30"] % 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, v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31", "z31"] % restricted_for_arm64ec,
p0: preg = ["p0"], p0: preg = ["p0"] % restricted_for_arm64ec,
p1: preg = ["p1"], p1: preg = ["p1"] % restricted_for_arm64ec,
p2: preg = ["p2"], p2: preg = ["p2"] % restricted_for_arm64ec,
p3: preg = ["p3"], p3: preg = ["p3"] % restricted_for_arm64ec,
p4: preg = ["p4"], p4: preg = ["p4"] % restricted_for_arm64ec,
p5: preg = ["p5"], p5: preg = ["p5"] % restricted_for_arm64ec,
p6: preg = ["p6"], p6: preg = ["p6"] % restricted_for_arm64ec,
p7: preg = ["p7"], p7: preg = ["p7"] % restricted_for_arm64ec,
p8: preg = ["p8"], p8: preg = ["p8"] % restricted_for_arm64ec,
p9: preg = ["p9"], p9: preg = ["p9"] % restricted_for_arm64ec,
p10: preg = ["p10"], p10: preg = ["p10"] % restricted_for_arm64ec,
p11: preg = ["p11"], p11: preg = ["p11"] % restricted_for_arm64ec,
p12: preg = ["p12"], p12: preg = ["p12"] % restricted_for_arm64ec,
p13: preg = ["p13"], p13: preg = ["p13"] % restricted_for_arm64ec,
p14: preg = ["p14"], p14: preg = ["p14"] % restricted_for_arm64ec,
p15: preg = ["p15"], p15: preg = ["p15"] % restricted_for_arm64ec,
ffr: preg = ["ffr"], ffr: preg = ["ffr"] % restricted_for_arm64ec,
#error = ["x19", "w19"] => #error = ["x19", "w19"] =>
"x19 is used internally by LLVM and cannot be used as an operand for inline asm", "x19 is used internally by LLVM and cannot be used as an operand for inline asm",
#error = ["x29", "w29", "fp", "wfp"] => #error = ["x29", "w29", "fp", "wfp"] =>
@ -200,12 +201,66 @@ impl AArch64InlineAsmReg {
_arch: InlineAsmArch, _arch: InlineAsmArch,
modifier: Option<char>, modifier: Option<char>,
) -> fmt::Result { ) -> fmt::Result {
let (prefix, index) = if (self as u32) < Self::v0 as u32 { let (prefix, index) = if let Some(index) = self.reg_index() {
(modifier.unwrap_or('x'), self as u32 - Self::x0 as u32) (modifier.unwrap_or('x'), index)
} else if let Some(index) = self.vreg_index() {
(modifier.unwrap_or('v'), index)
} else { } else {
(modifier.unwrap_or('v'), self as u32 - Self::v0 as u32) return out.write_str(self.name());
}; };
assert!(index < 32); assert!(index < 32);
write!(out, "{prefix}{index}") 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, Arm,
AArch64, AArch64,
AArch64NoX18, AArch64NoX18,
Arm64EC,
RiscV, RiscV,
LoongArch, LoongArch,
S390x, S390x,
@ -932,7 +933,7 @@ impl InlineAsmClobberAbi {
_ => Err(&["C", "system", "efiapi"]), _ => Err(&["C", "system", "efiapi"]),
}, },
InlineAsmArch::Arm64EC => match name { InlineAsmArch::Arm64EC => match name {
"C" | "system" => Ok(InlineAsmClobberAbi::AArch64NoX18), "C" | "system" => Ok(InlineAsmClobberAbi::Arm64EC),
_ => Err(&["C", "system"]), _ => Err(&["C", "system"]),
}, },
InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name { InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name {
@ -1033,7 +1034,6 @@ impl InlineAsmClobberAbi {
p0, p1, p2, p3, p4, p5, p6, p7, p0, p1, p2, p3, p4, p5, p6, p7,
p8, p9, p10, p11, p12, p13, p14, p15, p8, p9, p10, p11, p12, p13, p14, p15,
ffr, ffr,
} }
}, },
InlineAsmClobberAbi::AArch64NoX18 => clobbered_regs! { InlineAsmClobberAbi::AArch64NoX18 => clobbered_regs! {
@ -1052,7 +1052,20 @@ impl InlineAsmClobberAbi {
p0, p1, p2, p3, p4, p5, p6, p7, p0, p1, p2, p3, p4, p5, p6, p7,
p8, p9, p10, p11, p12, p13, p14, p15, p8, p9, p10, p11, p12, p13, p14, p15,
ffr, 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! { InlineAsmClobberAbi::Arm => clobbered_regs! {

View file

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

View file

@ -64,6 +64,7 @@
#![allow(missing_docs)] #![allow(missing_docs)]
use crate::marker::{DiscriminantKind, Tuple}; use crate::marker::{DiscriminantKind, Tuple};
use crate::mem::SizedTypeProperties;
use crate::{ptr, ub_checks}; use crate::{ptr, ub_checks};
pub mod mir; 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>(), size: usize = size_of::<T>(),
align: usize = align_of::<T>(), align: usize = align_of::<T>(),
count: usize = count, count: usize = count,
) => ) => {
ub_checks::is_aligned_and_not_null(src, align) let zero_size = count == 0 || size == 0;
&& 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)
&& ub_checks::is_nonoverlapping(src, dst, size, count) && ub_checks::is_nonoverlapping(src, dst, size, count)
}
); );
// SAFETY: the safety contract for `copy_nonoverlapping` must be // 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 (), src: *const () = src as *const (),
dst: *mut () = dst as *mut (), dst: *mut () = dst as *mut (),
align: usize = align_of::<T>(), 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(src, align, zero_size)
&& ub_checks::is_aligned_and_not_null(dst, align) && ub_checks::is_aligned_and_not_null(dst, align, zero_size)
); );
copy(src, dst, count) 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 (), addr: *const () = dst as *const (),
align: usize = align_of::<T>(), 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) 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" /// 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 /// implementations of `Ptr::DerefMut` are likewise ruled out by the contract of
/// `Pin::new_unchecked`. /// `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"] #[must_use = "`self` will be dropped if the result is not used"]
#[inline(always)] #[inline(always)]
pub fn as_deref_mut(self: Pin<&mut Pin<Ptr>>) -> Pin<&mut Ptr::Target> { 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::cmp::Ordering;
use crate::marker::FnPtr; use crate::marker::FnPtr;
use crate::mem::{self, MaybeUninit}; use crate::mem::{self, MaybeUninit, SizedTypeProperties};
use crate::{fmt, hash, intrinsics, ub_checks}; use crate::{fmt, hash, intrinsics, ub_checks};
mod alignment; 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>(), size: usize = size_of::<T>(),
align: usize = align_of::<T>(), align: usize = align_of::<T>(),
count: usize = count, count: usize = count,
) => ) => {
ub_checks::is_aligned_and_not_null(x, align) let zero_size = size == 0 || count == 0;
&& ub_checks::is_aligned_and_not_null(y, align) 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) && ub_checks::is_nonoverlapping(x, y, size, count)
}
); );
// Split up the slice into small power-of-two-sized chunks that LLVM is able // 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 (), addr: *const () = dst as *const (),
align: usize = align_of::<T>(), 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) mem::replace(&mut *dst, src)
} }
@ -1430,7 +1433,8 @@ pub const unsafe fn read<T>(src: *const T) -> T {
( (
addr: *const () = src as *const (), addr: *const () = src as *const (),
align: usize = align_of::<T>(), 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) 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 (), addr: *mut () = dst as *mut (),
align: usize = align_of::<T>(), 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) 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 (), addr: *const () = src as *const (),
align: usize = align_of::<T>(), 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) intrinsics::volatile_load(src)
} }
@ -1887,7 +1893,8 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
( (
addr: *mut () = dst as *mut (), addr: *mut () = dst as *mut (),
align: usize = align_of::<T>(), 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); 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>(), align: usize = align_of::<T>(),
len: usize = len, 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) && ub_checks::is_valid_allocation_size(size, len)
); );
&*ptr::slice_from_raw_parts(data, 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>(), align: usize = align_of::<T>(),
len: usize = len, 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) && ub_checks::is_valid_allocation_size(size, len)
); );
&mut *ptr::slice_from_raw_parts_mut(data, 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) intrinsics::ub_checks() && const_eval_select((), comptime, runtime)
} }
/// Checks whether `ptr` is properly aligned with respect to /// Checks whether `ptr` is properly aligned with respect to the given alignment, and
/// `align_of::<T>()`. /// if `is_zst == false`, that `ptr` is not null.
/// ///
/// In `const` this is approximate and can fail spuriously. It is primarily intended /// 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 /// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the
/// check is anyway not executed in `const`. /// check is anyway not executed in `const`.
#[inline] #[inline]
pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool { pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize, is_zst: bool) -> bool {
!ptr.is_null() && ptr.is_aligned_to(align) ptr.is_aligned_to(align) && (is_zst || !ptr.is_null())
} }
#[inline] #[inline]

View file

@ -43,6 +43,11 @@ use crate::common::{
use crate::header::HeadersCache; use crate::header::HeadersCache;
use crate::util::logv; 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 { pub fn parse_config(args: Vec<String>) -> Config {
let mut opts = Options::new(); let mut opts = Options::new();
opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH") 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>) { pub fn run_tests(config: Arc<Config>) {
// If we want to collect rustfix coverage information, // If we want to collect rustfix coverage information,
// we first make sure that the coverage file does not exist. // 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()); 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(); let mut tests = Vec::new();
for c in configs { for c in configs {
let mut found_paths = HashSet::new(); 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())); 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); let res = test::run_tests_console(&opts, tests);
// Check the outcome reported by libtest.
match res { match res {
Ok(true) => {} Ok(true) => {}
Ok(false) => { 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( pub fn make_tests(
config: Arc<Config>, config: Arc<Config>,
tests: &mut Vec<test::TestDescAndFn>, tests: &mut Vec<test::TestDescAndFn>,
@ -610,10 +628,17 @@ fn common_inputs_stamp(config: &Config) -> Stamp {
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> { 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 { if !config.only_modified {
return Ok(vec![]); return Ok(vec![]);
} }
let files = let files =
get_git_modified_files(&config.git_config(), Some(dir), &vec!["rs", "stderr", "fixed"])? get_git_modified_files(&config.git_config(), Some(dir), &vec!["rs", "stderr", "fixed"])?
.unwrap_or(vec![]); .unwrap_or(vec![]);
@ -634,6 +659,8 @@ fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> {
Ok(full_paths) 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( fn collect_tests_from_dir(
config: Arc<Config>, config: Arc<Config>,
cache: &HeadersCache, cache: &HeadersCache,
@ -650,6 +677,8 @@ fn collect_tests_from_dir(
return Ok(()); 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 config.mode == Mode::RunMake {
if dir.join("Makefile").exists() && dir.join("rmake.rs").exists() { if dir.join("Makefile").exists() && dir.join("rmake.rs").exists() {
return Err(io::Error::other( return Err(io::Error::other(
@ -663,6 +692,7 @@ fn collect_tests_from_dir(
relative_dir: relative_dir_path.parent().unwrap().to_path_buf(), relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
}; };
tests.extend(make_test(config, cache, &paths, inputs, poisoned)); 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(()); return Ok(());
} }
} }
@ -677,22 +707,27 @@ fn collect_tests_from_dir(
fs::create_dir_all(&build_dir).unwrap(); fs::create_dir_all(&build_dir).unwrap();
// Add each `.rs` file as a test, and recurse further on any // 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 // FIXME: this walks full tests tree, even if we have something to ignore
// use walkdir/ignore like in tidy? // use walkdir/ignore like in tidy?
for file in fs::read_dir(dir)? { for file in fs::read_dir(dir)? {
let file = file?; let file = file?;
let file_path = file.path(); let file_path = file.path();
let file_name = file.file_name(); let file_name = file.file_name();
if is_test(&file_name) && (!config.only_modified || modified_tests.contains(&file_path)) { 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()); 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()); let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap());
found_paths.insert(rel_test_path); found_paths.insert(rel_test_path);
let paths = let paths =
TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
tests.extend(make_test(config.clone(), cache, &paths, inputs, poisoned)) tests.extend(make_test(config.clone(), cache, &paths, inputs, poisoned))
} else if file_path.is_dir() { } 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()); let relative_file_path = relative_dir_path.join(file.file_name());
if &file_name != "auxiliary" { if &file_name != "auxiliary" {
debug!("found directory: {:?}", file_path.display()); 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)) !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( fn make_test(
config: Arc<Config>, config: Arc<Config>,
cache: &HeadersCache, cache: &HeadersCache,
@ -735,6 +772,9 @@ fn make_test(
inputs: &Stamp, inputs: &Stamp,
poisoned: &mut bool, poisoned: &mut bool,
) -> Vec<test::TestDescAndFn> { ) -> 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 { let test_path = if config.mode == Mode::RunMake {
if testpaths.file.join("rmake.rs").exists() && testpaths.file.join("Makefile").exists() { if testpaths.file.join("rmake.rs").exists() && testpaths.file.join("Makefile").exists() {
panic!("run-make tests cannot have both `rmake.rs` and `Makefile`"); panic!("run-make tests cannot have both `rmake.rs` and `Makefile`");
@ -750,26 +790,40 @@ fn make_test(
} else { } else {
PathBuf::from(&testpaths.file) PathBuf::from(&testpaths.file)
}; };
// Scan the test file to discover its revisions, if any.
let early_props = EarlyProps::from_file(&config, &test_path); let early_props = EarlyProps::from_file(&config, &test_path);
// Incremental tests are special, they inherently cannot be run in parallel. // Normally we create one libtest structure per revision, with two exceptions:
// `runtest::run` will be responsible for iterating over revisions. // - 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 { let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
vec![None] vec![None]
} else { } else {
early_props.revisions.iter().map(|r| Some(r.as_str())).collect() 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 revisions
.into_iter() .into_iter()
.map(|revision| { .map(|revision| {
// Create a test name and description to hand over to libtest.
let src_file = let src_file =
std::fs::File::open(&test_path).expect("open test file to parse ignores"); std::fs::File::open(&test_path).expect("open test file to parse ignores");
let test_name = crate::make_test_name(&config, testpaths, revision); 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( let mut desc = make_test_description(
&config, cache, test_name, &test_path, src_file, revision, poisoned, &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 if !config.force_rerun
&& is_up_to_date(&config, testpaths, &early_props, revision, inputs) && 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. // Keep this in sync with the "up-to-date" message detected by bootstrap.
desc.ignore_message = Some("up-to-date"); desc.ignore_message = Some("up-to-date");
} }
test::TestDescAndFn {
desc, // Create the callback that will run this test/revision when libtest calls it.
testfn: make_test_closure(config.clone(), testpaths, revision), let testfn = make_test_closure(config.clone(), testpaths, revision);
}
test::TestDescAndFn { desc, testfn }
}) })
.collect() .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 { fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
output_base_dir(config, testpaths, revision).join("stamp") 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( fn files_related_to_test(
config: &Config, config: &Config,
testpaths: &TestPaths, testpaths: &TestPaths,
@ -824,46 +885,61 @@ fn files_related_to_test(
related 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( fn is_up_to_date(
config: &Config, config: &Config,
testpaths: &TestPaths, testpaths: &TestPaths,
props: &EarlyProps, props: &EarlyProps,
revision: Option<&str>, revision: Option<&str>,
inputs: &Stamp, inputs: &Stamp, // Last-modified timestamp of the compiler, compiletest etc
) -> bool { ) -> bool {
let stamp_name = stamp(config, testpaths, revision); 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) { let contents = match fs::read_to_string(&stamp_name) {
Ok(f) => f, Ok(f) => f,
Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"), 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, Err(_) => return false,
}; };
let expected_hash = runtest::compute_stamp_hash(config); let expected_hash = runtest::compute_stamp_hash(config);
if contents != expected_hash { 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; 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(); let mut inputs = inputs.clone();
for path in files_related_to_test(config, testpaths, props, revision) { for path in files_related_to_test(config, testpaths, props, revision) {
inputs.add_path(&path); 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) inputs < Stamp::from_path(&stamp_name)
} }
/// The maximum of a set of file-modified timestamps.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Stamp { struct Stamp {
time: SystemTime, time: SystemTime,
} }
impl Stamp { impl Stamp {
/// Creates a timestamp holding the last-modified time of the specified file.
fn from_path(path: &Path) -> Self { fn from_path(path: &Path) -> Self {
let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH }; let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH };
stamp.add_path(path); stamp.add_path(path);
stamp 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) { fn add_path(&mut self, path: &Path) {
let modified = fs::metadata(path) let modified = fs::metadata(path)
.and_then(|metadata| metadata.modified()) .and_then(|metadata| metadata.modified())
@ -871,6 +947,9 @@ impl Stamp {
self.time = self.time.max(modified); 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) { fn add_dir(&mut self, path: &Path) {
for entry in WalkDir::new(path) { for entry in WalkDir::new(path) {
let entry = entry.unwrap(); 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( fn make_test_name(
config: &Config, config: &Config,
testpaths: &TestPaths, 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( fn make_test_closure(
config: Arc<Config>, config: Arc<Config>,
testpaths: &TestPaths, testpaths: &TestPaths,
revision: Option<&str>, revision: Option<&str>,
) -> test::TestFn { ) -> test::TestFn {
let config = config.clone();
let testpaths = testpaths.clone(); let testpaths = testpaths.clone();
let revision = revision.map(str::to_owned); 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 || { test::DynTestFn(Box::new(move || {
runtest::run(config, &testpaths, revision.as_deref()); runtest::run(config, &testpaths, revision.as_deref());
Ok(()) 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>) { fn check_overlapping_tests(found_paths: &HashSet<PathBuf>) {
let mut collisions = Vec::new(); let mut collisions = Vec::new();
for path in found_paths { 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 //@ is "$.index[*][?(@.name=='f1')].inner.function.header.is_unsafe" true
pub fn f1(); 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" { 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 { extern {
async fn L() { //~ ERROR: incorrect function inside `extern` block 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() {} 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 = 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 = 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 --> $DIR/issue-95829.rs:4:5
| |
LL | extern { LL | extern {
| ------ in this `extern` block | ------ in this `extern` block
LL | async fn L() { LL | async fn L() {
| ^^^^^ help: remove this qualifier | ^^^^^ help: remove the `async` qualifier
error: aborting due to 2 previous errors 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" { extern "C" {
async fn fe1(); //~ ERROR functions in `extern` blocks cannot have qualifiers async fn fe1(); //~ ERROR functions in `extern` blocks cannot
unsafe fn fe2(); //~ ERROR items in unadorned `extern` blocks cannot have safety qualifiers unsafe fn fe2(); //~ ERROR items in `extern` blocks without an `unsafe` qualifier cannot
const fn fe3(); //~ ERROR functions in `extern` blocks cannot have qualifiers const fn fe3(); //~ ERROR functions in `extern` blocks cannot
extern "C" fn fe4(); //~ ERROR functions in `extern` blocks cannot have qualifiers extern "C" fn fe4(); //~ ERROR functions in `extern` blocks cannot
const async unsafe extern "C" fn fe5(); const async unsafe extern "C" fn fe5();
//~^ ERROR functions in `extern` blocks //~^ ERROR functions in `extern` blocks
//~| ERROR functions in `extern` blocks //~| ERROR functions in `extern` blocks
//~| ERROR functions in `extern` blocks //~| ERROR functions in `extern` blocks
//~| ERROR functions cannot be both `const` and `async` //~| 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 | | `async` because of this
| `const` 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 --> $DIR/fn-header-semantic-fail.rs:44:9
| |
LL | extern "C" { LL | extern "C" {
| ---------- in this `extern` block | ---------- in this `extern` block
LL | async fn fe1(); 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 --> $DIR/fn-header-semantic-fail.rs:45:9
| |
LL | unsafe fn fe2(); LL | unsafe fn fe2();
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
| |
help: add unsafe to this `extern` block help: add `unsafe` to this `extern` block
| |
LL | unsafe extern "C" { 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 --> $DIR/fn-header-semantic-fail.rs:46:9
| |
LL | extern "C" { LL | extern "C" {
| ---------- in this `extern` block | ---------- in this `extern` block
... ...
LL | const fn fe3(); 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 --> $DIR/fn-header-semantic-fail.rs:47:9
| |
LL | extern "C" { LL | extern "C" {
| ---------- in this `extern` block | ---------- in this `extern` block
... ...
LL | extern "C" fn fe4(); 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 --> $DIR/fn-header-semantic-fail.rs:48:15
| |
LL | extern "C" { LL | extern "C" {
| ---------- in this `extern` block | ---------- in this `extern` block
... ...
LL | const async unsafe extern "C" fn fe5(); 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 --> $DIR/fn-header-semantic-fail.rs:48:9
| |
LL | extern "C" { LL | extern "C" {
| ---------- in this `extern` block | ---------- in this `extern` block
... ...
LL | const async unsafe extern "C" fn fe5(); 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 --> $DIR/fn-header-semantic-fail.rs:48:28
| |
LL | extern "C" { LL | extern "C" {
| ---------- in this `extern` block | ---------- in this `extern` block
... ...
LL | const async unsafe extern "C" fn fe5(); 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 --> $DIR/fn-header-semantic-fail.rs:48:9
| |
LL | const async unsafe extern "C" fn fe5(); 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" { LL | unsafe extern "C" {
| ++++++ | ++++++

View file

@ -1,9 +1,9 @@
extern "C" { extern "C" {
const fn foo(); const fn foo();
//~^ ERROR functions in `extern` blocks cannot have qualifiers //~^ ERROR functions in `extern` blocks cannot
const unsafe fn bar(); const unsafe fn bar();
//~^ ERROR functions in `extern` blocks cannot have qualifiers //~^ ERROR functions in `extern` blocks cannot
//~| ERROR items in unadorned `extern` blocks cannot have safety qualifiers //~| ERROR items in `extern` blocks without an `unsafe` qualifier cannot
} }
fn main() {} 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 --> $DIR/no-const-fn-in-extern-block.rs:2:5
| |
LL | extern "C" { LL | extern "C" {
| ---------- in this `extern` block | ---------- in this `extern` block
LL | const fn foo(); 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 --> $DIR/no-const-fn-in-extern-block.rs:4:5
| |
LL | extern "C" { LL | extern "C" {
| ---------- in this `extern` block | ---------- in this `extern` block
... ...
LL | const unsafe fn bar(); 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 --> $DIR/no-const-fn-in-extern-block.rs:4:5
| |
LL | const unsafe fn bar(); LL | const unsafe fn bar();
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
| |
help: add unsafe to this `extern` block help: add `unsafe` to this `extern` block
| |
LL | unsafe extern "C" { 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 --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:8:5
| |
LL | safe static TEST1: i32; LL | safe static TEST1: i32;
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^
| |
help: add unsafe to this `extern` block help: add `unsafe` to this `extern` block
| |
LL | unsafe extern "C" { 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 --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5
| |
LL | safe fn test1(i: i32); LL | safe fn test1(i: i32);
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
| |
help: add unsafe to this `extern` block help: add `unsafe` to this `extern` block
| |
LL | unsafe extern "C" { LL | unsafe extern "C" {
| ++++++ | ++++++

View file

@ -10,24 +10,24 @@ LL | |
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 --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:8:5
| |
LL | safe static TEST1: i32; LL | safe static TEST1: i32;
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^
| |
help: add unsafe to this `extern` block help: add `unsafe` to this `extern` block
| |
LL | unsafe extern "C" { 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 --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5
| |
LL | safe fn test1(i: i32); LL | safe fn test1(i: i32);
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
| |
help: add unsafe to this `extern` block help: add `unsafe` to this `extern` block
| |
LL | unsafe extern "C" { LL | unsafe extern "C" {
| ++++++ | ++++++

View file

@ -6,9 +6,9 @@
extern "C" { extern "C" {
//[edition2024]~^ ERROR extern blocks must be unsafe //[edition2024]~^ ERROR extern blocks must be unsafe
safe static TEST1: i32; 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); 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() { fn test2() {

View file

@ -3,7 +3,7 @@
#![allow(dead_code)] #![allow(dead_code)]
unsafe extern "C" { 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() {} fn main() {}

View file

@ -3,7 +3,7 @@
#![allow(dead_code)] #![allow(dead_code)]
extern "C" { 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() {} 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 --> $DIR/unsafe-on-extern-block-issue-126756.rs:6:5
| |
LL | unsafe fn foo(); LL | unsafe fn foo();
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
| |
help: add unsafe to this `extern` block help: add `unsafe` to this `extern` block
| |
LL | unsafe extern "C" { LL | unsafe extern "C" {
| ++++++ | ++++++

View file

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