1
Fork 0

Auto merge of #139845 - Zalathar:rollup-u5u5y1v, r=Zalathar

Rollup of 17 pull requests

Successful merges:

 - #138374 (Enable contracts for const functions)
 - #138380 (ci: add runners for vanilla LLVM 20)
 - #138393 (Allow const patterns of matches to contain pattern types)
 - #139517 (std: sys: process: uefi: Use NULL stdin by default)
 - #139554 (std: add Output::exit_ok)
 - #139660 (compiletest: Add an experimental new executor to replace libtest)
 - #139669 (Overhaul `AssocItem`)
 - #139671 (Proc macro span API redesign: Replace proc_macro::SourceFile by Span::{file, local_file})
 - #139750 (std/thread: Use default stack size from menuconfig for NuttX)
 - #139772 (Remove `hir::Map`)
 - #139785 (Let CStrings be either 1 or 2 byte aligned.)
 - #139789 (do not unnecessarily leak auto traits in item bounds)
 - #139791 (drop global where-bounds before merging candidates)
 - #139798 (normalize: prefer `ParamEnv` over `AliasBound` candidates)
 - #139822 (Fix: Map EOPNOTSUPP to ErrorKind::Unsupported on Unix)
 - #139833 (Fix some HIR pretty-printing problems)
 - #139836 (Basic tests of MPMC receiver cloning)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-04-15 08:02:23 +00:00
commit f433fa46b0
173 changed files with 2499 additions and 994 deletions

View file

@ -2,19 +2,23 @@
pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_requires as requires};
/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }`
/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }`
/// (including the implicit return of the tail expression, if any).
/// This is an identity function used as part of the desugaring of the `#[ensures]` attribute.
///
/// This is an existing hack to allow users to omit the type of the return value in their ensures
/// attribute.
///
/// Ideally, rustc should be able to generate the type annotation.
/// The existing lowering logic makes it rather hard to add the explicit type annotation,
/// while the function call is fairly straight forward.
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
// Similar to `contract_check_requires`, we need to use the user-facing
// `contracts` feature rather than the perma-unstable `contracts_internals`.
// Const-checking doesn't honor allow_internal_unstable logic used by contract expansion.
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[lang = "contract_build_check_ensures"]
#[track_caller]
pub fn build_check_ensures<Ret, C>(cond: C) -> impl (Fn(Ret) -> Ret) + Copy
pub const fn build_check_ensures<Ret, C>(cond: C) -> C
where
C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static,
C: Fn(&Ret) -> bool + Copy + 'static,
{
#[track_caller]
move |ret| {
crate::intrinsics::contract_check_ensures(&ret, cond);
ret
}
cond
}

View file

@ -3402,20 +3402,62 @@ pub const fn contract_checks() -> bool {
///
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
/// returns false.
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
///
/// Note that this function is a no-op during constant evaluation.
#[unstable(feature = "contracts_internals", issue = "128044")]
// Calls to this function get inserted by an AST expansion pass, which uses the equivalent of
// `#[allow_internal_unstable]` to allow using `contracts_internals` functions. Const-checking
// doesn't honor `#[allow_internal_unstable]`, so for the const feature gate we use the user-facing
// `contracts` feature rather than the perma-unstable `contracts_internals`
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[lang = "contract_check_requires"]
#[rustc_intrinsic]
pub fn contract_check_requires<C: Fn() -> bool>(cond: C) {
if contract_checks() && !cond() {
// Emit no unwind panic in case this was a safety requirement.
crate::panicking::panic_nounwind("failed requires check");
}
pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
const_eval_select!(
@capture[C: Fn() -> bool + Copy] { cond: C } :
if const {
// Do nothing
} else {
if contract_checks() && !cond() {
// Emit no unwind panic in case this was a safety requirement.
crate::panicking::panic_nounwind("failed requires check");
}
}
)
}
/// Check if the post-condition `cond` has been met.
///
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
/// returns false.
///
/// Note that this function is a no-op during constant evaluation.
#[cfg(not(bootstrap))]
#[unstable(feature = "contracts_internals", issue = "128044")]
// Similar to `contract_check_requires`, we need to use the user-facing
// `contracts` feature rather than the perma-unstable `contracts_internals`.
// Const-checking doesn't honor allow_internal_unstable logic used by contract expansion.
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[lang = "contract_check_ensures"]
#[rustc_intrinsic]
pub const fn contract_check_ensures<C: Fn(&Ret) -> bool + Copy, Ret>(cond: C, ret: Ret) -> Ret {
const_eval_select!(
@capture[C: Fn(&Ret) -> bool + Copy, Ret] { cond: C, ret: Ret } -> Ret :
if const {
// Do nothing
ret
} else {
if contract_checks() && !cond(&ret) {
// Emit no unwind panic in case this was a safety requirement.
crate::panicking::panic_nounwind("failed ensures check");
}
ret
}
)
}
/// This is the old version of contract_check_ensures kept here for bootstrap only.
#[cfg(bootstrap)]
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
#[rustc_intrinsic]
pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) {

View file

@ -101,7 +101,6 @@
#![feature(bstr)]
#![feature(bstr_internals)]
#![feature(cfg_match)]
#![feature(closure_track_caller)]
#![feature(const_carrying_mul_add)]
#![feature(const_eval_select)]
#![feature(core_intrinsics)]

View file

@ -111,12 +111,6 @@ impl Clone for TokenStream {
}
}
impl Clone for SourceFile {
fn clone(&self) -> Self {
self.clone()
}
}
impl Span {
pub(crate) fn def_site() -> Span {
Bridge::with(|bridge| bridge.globals.def_site)

View file

@ -81,16 +81,8 @@ macro_rules! with_api {
$self: $S::TokenStream
) -> Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol>>;
},
SourceFile {
fn drop($self: $S::SourceFile);
fn clone($self: &$S::SourceFile) -> $S::SourceFile;
fn eq($self: &$S::SourceFile, other: &$S::SourceFile) -> bool;
fn path($self: &$S::SourceFile) -> String;
fn is_real($self: &$S::SourceFile) -> bool;
},
Span {
fn debug($self: $S::Span) -> String;
fn source_file($self: $S::Span) -> $S::SourceFile;
fn parent($self: $S::Span) -> Option<$S::Span>;
fn source($self: $S::Span) -> $S::Span;
fn byte_range($self: $S::Span) -> Range<usize>;
@ -98,6 +90,8 @@ macro_rules! with_api {
fn end($self: $S::Span) -> $S::Span;
fn line($self: $S::Span) -> usize;
fn column($self: $S::Span) -> usize;
fn file($self: $S::Span) -> String;
fn local_file($self: $S::Span) -> Option<String>;
fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>;
fn subspan($self: $S::Span, start: Bound<usize>, end: Bound<usize>) -> Option<$S::Span>;
fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span;
@ -120,7 +114,6 @@ macro_rules! with_api_handle_types {
'owned:
FreeFunctions,
TokenStream,
SourceFile,
'interned:
Span,

View file

@ -82,7 +82,6 @@ with_api_handle_types!(define_server_handles);
pub trait Types {
type FreeFunctions: 'static;
type TokenStream: 'static + Clone;
type SourceFile: 'static + Clone;
type Span: 'static + Copy + Eq + Hash;
type Symbol: 'static;
}

View file

@ -491,12 +491,6 @@ impl Span {
Span(bridge::client::Span::mixed_site())
}
/// The original source file into which this span points.
#[unstable(feature = "proc_macro_span", issue = "54725")]
pub fn source_file(&self) -> SourceFile {
SourceFile(self.0.source_file())
}
/// The `Span` for the tokens in the previous macro expansion from which
/// `self` was generated from, if any.
#[unstable(feature = "proc_macro_span", issue = "54725")]
@ -546,6 +540,25 @@ impl Span {
self.0.column()
}
/// The path to the source file in which this span occurs, for display purposes.
///
/// This might not correspond to a valid file system path.
/// It might be remapped, or might be an artificial path such as `"<macro expansion>"`.
#[unstable(feature = "proc_macro_span", issue = "54725")]
pub fn file(&self) -> String {
self.0.file()
}
/// The path to the source file in which this span occurs on disk.
///
/// This is the actual path on disk. It is unaffected by path remapping.
///
/// This path should not be embedded in the output of the macro; prefer `file()` instead.
#[unstable(feature = "proc_macro_span", issue = "54725")]
pub fn local_file(&self) -> Option<PathBuf> {
self.0.local_file().map(|s| PathBuf::from(s))
}
/// Creates a new span encompassing `self` and `other`.
///
/// Returns `None` if `self` and `other` are from different files.
@ -614,58 +627,6 @@ impl fmt::Debug for Span {
}
}
/// The source file of a given `Span`.
#[unstable(feature = "proc_macro_span", issue = "54725")]
#[derive(Clone)]
pub struct SourceFile(bridge::client::SourceFile);
impl SourceFile {
/// Gets the path to this source file.
///
/// ### Note
/// If the code span associated with this `SourceFile` was generated by an external macro, this
/// macro, this might not be an actual path on the filesystem. Use [`is_real`] to check.
///
/// Also note that even if `is_real` returns `true`, if `--remap-path-prefix` was passed on
/// the command line, the path as given might not actually be valid.
///
/// [`is_real`]: Self::is_real
#[unstable(feature = "proc_macro_span", issue = "54725")]
pub fn path(&self) -> PathBuf {
PathBuf::from(self.0.path())
}
/// Returns `true` if this source file is a real source file, and not generated by an external
/// macro's expansion.
#[unstable(feature = "proc_macro_span", issue = "54725")]
pub fn is_real(&self) -> bool {
// This is a hack until intercrate spans are implemented and we can have real source files
// for spans generated in external macros.
// https://github.com/rust-lang/rust/pull/43604#issuecomment-333334368
self.0.is_real()
}
}
#[unstable(feature = "proc_macro_span", issue = "54725")]
impl fmt::Debug for SourceFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SourceFile")
.field("path", &self.path())
.field("is_real", &self.is_real())
.finish()
}
}
#[unstable(feature = "proc_macro_span", issue = "54725")]
impl PartialEq for SourceFile {
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}
#[unstable(feature = "proc_macro_span", issue = "54725")]
impl Eq for SourceFile {}
/// A single token or a delimited sequence of token trees (e.g., `[1, (), ..]`).
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
#[derive(Clone)]

View file

@ -1286,6 +1286,40 @@ pub struct Output {
pub stderr: Vec<u8>,
}
impl Output {
/// Returns an error if a nonzero exit status was received.
///
/// If the [`Command`] exited successfully,
/// `self` is returned.
///
/// This is equivalent to calling [`exit_ok`](ExitStatus::exit_ok)
/// on [`Output.status`](Output::status).
///
/// Note that this will throw away the [`Output::stderr`] field in the error case.
/// If the child process outputs useful informantion to stderr, you can:
/// * Use `cmd.stderr(Stdio::inherit())` to forward the
/// stderr child process to the parent's stderr,
/// usually printing it to console where the user can see it.
/// This is usually correct for command-line applications.
/// * Capture `stderr` using a custom error type.
/// This is usually correct for libraries.
///
/// # Examples
///
/// ```
/// #![feature(exit_status_error)]
/// # #[cfg(unix)] {
/// use std::process::Command;
/// assert!(Command::new("false").output().unwrap().exit_ok().is_err());
/// # }
/// ```
#[unstable(feature = "exit_status_error", issue = "84908")]
pub fn exit_ok(self) -> Result<Self, ExitStatusError> {
self.status.exit_ok()?;
Ok(self)
}
}
// If either stderr or stdout are valid utf8 strings it prints the valid
// strings, otherwise it prints the byte sequence instead
#[stable(feature = "process_output_debug", since = "1.7.0")]

View file

@ -274,6 +274,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
libc::ETXTBSY => ExecutableFileBusy,
libc::EXDEV => CrossesDevices,
libc::EINPROGRESS => InProgress,
libc::EOPNOTSUPP => Unsupported,
libc::EACCES | libc::EPERM => PermissionDenied,

View file

@ -8,14 +8,19 @@ use crate::sys::weak::weak;
use crate::sys::{os, stack_overflow};
use crate::time::Duration;
use crate::{cmp, io, ptr};
#[cfg(not(any(target_os = "l4re", target_os = "vxworks", target_os = "espidf")))]
#[cfg(not(any(
target_os = "l4re",
target_os = "vxworks",
target_os = "espidf",
target_os = "nuttx"
)))]
pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
#[cfg(target_os = "l4re")]
pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
#[cfg(target_os = "vxworks")]
pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024;
#[cfg(target_os = "espidf")]
pub const DEFAULT_MIN_STACK_SIZE: usize = 0; // 0 indicates that the stack size configured in the ESP-IDF menuconfig system should be used
#[cfg(any(target_os = "espidf", target_os = "nuttx"))]
pub const DEFAULT_MIN_STACK_SIZE: usize = 0; // 0 indicates that the stack size configured in the ESP-IDF/NuttX menuconfig system should be used
#[cfg(target_os = "fuchsia")]
mod zircon {
@ -52,10 +57,10 @@ impl Thread {
let mut attr: mem::MaybeUninit<libc::pthread_attr_t> = mem::MaybeUninit::uninit();
assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0);
#[cfg(target_os = "espidf")]
#[cfg(any(target_os = "espidf", target_os = "nuttx"))]
if stack > 0 {
// Only set the stack if a non-zero value is passed
// 0 is used as an indication that the default stack size configured in the ESP-IDF menuconfig system should be used
// 0 is used as an indication that the default stack size configured in the ESP-IDF/NuttX menuconfig system should be used
assert_eq!(
libc::pthread_attr_setstacksize(
attr.as_mut_ptr(),
@ -65,7 +70,7 @@ impl Thread {
);
}
#[cfg(not(target_os = "espidf"))]
#[cfg(not(any(target_os = "espidf", target_os = "nuttx")))]
{
let stack_size = cmp::max(stack, min_stack_size(attr.as_ptr()));

View file

@ -1,4 +1,4 @@
use r_efi::protocols::simple_text_output;
use r_efi::protocols::{simple_text_input, simple_text_output};
use crate::collections::BTreeMap;
pub use crate::ffi::OsString as EnvKey;
@ -23,6 +23,7 @@ pub struct Command {
args: Vec<OsString>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
stdin: Option<Stdio>,
env: CommandEnv,
}
@ -48,6 +49,7 @@ impl Command {
args: Vec::new(),
stdout: None,
stderr: None,
stdin: None,
env: Default::default(),
}
}
@ -64,8 +66,8 @@ impl Command {
panic!("unsupported")
}
pub fn stdin(&mut self, _stdin: Stdio) {
panic!("unsupported")
pub fn stdin(&mut self, stdin: Stdio) {
self.stdin = Some(stdin);
}
pub fn stdout(&mut self, stdout: Stdio) {
@ -122,6 +124,22 @@ impl Command {
}
}
fn create_stdin(
s: Stdio,
) -> io::Result<Option<helpers::OwnedProtocol<uefi_command_internal::InputProtocol>>> {
match s {
Stdio::Null => unsafe {
helpers::OwnedProtocol::create(
uefi_command_internal::InputProtocol::null(),
simple_text_input::PROTOCOL_GUID,
)
}
.map(Some),
Stdio::Inherit => Ok(None),
Stdio::MakePipe => unsupported(),
}
}
pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
let mut cmd = uefi_command_internal::Image::load_image(&self.prog)?;
@ -149,6 +167,15 @@ impl Command {
cmd.stderr_inherit()
};
// Setup Stdin
let stdin = self.stdin.unwrap_or(Stdio::Null);
let stdin = Self::create_stdin(stdin)?;
if let Some(con) = stdin {
cmd.stdin_init(con)
} else {
cmd.stdin_inherit()
};
let env = env_changes(&self.env);
// Set any new vars
@ -334,7 +361,7 @@ impl<'a> fmt::Debug for CommandArgs<'a> {
#[allow(dead_code)]
mod uefi_command_internal {
use r_efi::protocols::{loaded_image, simple_text_output};
use r_efi::protocols::{loaded_image, simple_text_input, simple_text_output};
use crate::ffi::{OsStr, OsString};
use crate::io::{self, const_error};
@ -350,6 +377,7 @@ mod uefi_command_internal {
handle: NonNull<crate::ffi::c_void>,
stdout: Option<helpers::OwnedProtocol<PipeProtocol>>,
stderr: Option<helpers::OwnedProtocol<PipeProtocol>>,
stdin: Option<helpers::OwnedProtocol<InputProtocol>>,
st: OwnedTable<r_efi::efi::SystemTable>,
args: Option<(*mut u16, usize)>,
}
@ -384,7 +412,14 @@ mod uefi_command_internal {
helpers::open_protocol(child_handle, loaded_image::PROTOCOL_GUID).unwrap();
let st = OwnedTable::from_table(unsafe { (*loaded_image.as_ptr()).system_table });
Ok(Self { handle: child_handle, stdout: None, stderr: None, st, args: None })
Ok(Self {
handle: child_handle,
stdout: None,
stderr: None,
stdin: None,
st,
args: None,
})
}
}
@ -445,6 +480,17 @@ mod uefi_command_internal {
}
}
fn set_stdin(
&mut self,
handle: r_efi::efi::Handle,
protocol: *mut simple_text_input::Protocol,
) {
unsafe {
(*self.st.as_mut_ptr()).console_in_handle = handle;
(*self.st.as_mut_ptr()).con_in = protocol;
}
}
pub fn stdout_init(&mut self, protocol: helpers::OwnedProtocol<PipeProtocol>) {
self.set_stdout(
protocol.handle().as_ptr(),
@ -471,6 +517,19 @@ mod uefi_command_internal {
unsafe { self.set_stderr((*st.as_ptr()).standard_error_handle, (*st.as_ptr()).std_err) }
}
pub(crate) fn stdin_init(&mut self, protocol: helpers::OwnedProtocol<InputProtocol>) {
self.set_stdin(
protocol.handle().as_ptr(),
protocol.as_ref() as *const InputProtocol as *mut simple_text_input::Protocol,
);
self.stdin = Some(protocol);
}
pub(crate) fn stdin_inherit(&mut self) {
let st: NonNull<r_efi::efi::SystemTable> = system_table().cast();
unsafe { self.set_stdin((*st.as_ptr()).console_in_handle, (*st.as_ptr()).con_in) }
}
pub fn stderr(&self) -> io::Result<Vec<u8>> {
match &self.stderr {
Some(stderr) => stderr.as_ref().utf8(),
@ -722,6 +781,56 @@ mod uefi_command_internal {
}
}
#[repr(C)]
pub(crate) struct InputProtocol {
reset: simple_text_input::ProtocolReset,
read_key_stroke: simple_text_input::ProtocolReadKeyStroke,
wait_for_key: r_efi::efi::Event,
}
impl InputProtocol {
pub(crate) fn null() -> Self {
let evt = helpers::OwnedEvent::new(
r_efi::efi::EVT_NOTIFY_WAIT,
r_efi::efi::TPL_CALLBACK,
Some(Self::empty_notify),
None,
)
.unwrap();
Self {
reset: Self::null_reset,
read_key_stroke: Self::null_read_key,
wait_for_key: evt.into_raw(),
}
}
extern "efiapi" fn null_reset(
_: *mut simple_text_input::Protocol,
_: r_efi::efi::Boolean,
) -> r_efi::efi::Status {
r_efi::efi::Status::SUCCESS
}
extern "efiapi" fn null_read_key(
_: *mut simple_text_input::Protocol,
_: *mut simple_text_input::InputKey,
) -> r_efi::efi::Status {
r_efi::efi::Status::UNSUPPORTED
}
extern "efiapi" fn empty_notify(_: r_efi::efi::Event, _: *mut crate::ffi::c_void) {}
}
impl Drop for InputProtocol {
fn drop(&mut self) {
// Close wait_for_key
unsafe {
let _ = helpers::OwnedEvent::from_raw(self.wait_for_key);
}
}
}
pub fn create_args(prog: &OsStr, args: &[OsString]) -> Box<[u16]> {
const QUOTE: u16 = 0x0022;
const SPACE: u16 = 0x0020;

View file

@ -142,8 +142,12 @@ impl io::Write for Stderr {
// UTF-16 character should occupy 4 bytes at most in UTF-8
pub const STDIN_BUF_SIZE: usize = 4;
pub fn is_ebadf(_err: &io::Error) -> bool {
false
pub fn is_ebadf(err: &io::Error) -> bool {
if let Some(x) = err.raw_os_error() {
r_efi::efi::Status::UNSUPPORTED.as_usize() == x
} else {
false
}
}
pub fn panic_output() -> Option<impl io::Write> {

View file

@ -63,6 +63,24 @@ fn smoke_port_gone() {
assert!(tx.send(1).is_err());
}
#[test]
fn smoke_receiver_clone() {
let (tx, rx) = channel::<i32>();
let rx2 = rx.clone();
drop(rx);
tx.send(1).unwrap();
assert_eq!(rx2.recv().unwrap(), 1);
}
#[test]
fn smoke_receiver_clone_port_gone() {
let (tx, rx) = channel::<i32>();
let rx2 = rx.clone();
drop(rx);
drop(rx2);
assert!(tx.send(1).is_err());
}
#[test]
fn smoke_shared_port_gone() {
let (tx, rx) = channel::<i32>();
@ -124,6 +142,18 @@ fn chan_gone_concurrent() {
while rx.recv().is_ok() {}
}
#[test]
fn receiver_cloning() {
let (tx, rx) = channel::<i32>();
let rx2 = rx.clone();
tx.send(1).unwrap();
tx.send(2).unwrap();
assert_eq!(rx2.recv(), Ok(1));
assert_eq!(rx.recv(), Ok(2));
}
#[test]
fn stress() {
let count = if cfg!(miri) { 100 } else { 10000 };