Add more from the std

This commit is contained in:
2026-03-19 10:04:21 +01:00
parent 9b8afd2c5c
commit fc3a04a20e
49 changed files with 5497 additions and 424 deletions

6
.gitmodules vendored Normal file
View File

@@ -0,0 +1,6 @@
[submodule "backtrace-rs"]
path = backtrace-rs
url = https://github.com/rust-lang/backtrace-rs.git
[submodule "crates/std/crates/backtrace-rs"]
path = crates/std/crates/backtrace-rs
url = https://github.com/rust-lang/backtrace-rs.git

View File

@@ -1,6 +1,7 @@
[workspace] [workspace]
resolver = "3" resolver = "3"
members = ["crates/bytes-struct","crates/io","crates/std", "crates/shared", "user/*"] members = ["crates/bytes-struct","crates/io", "crates/shared", "user/*"]
exclude = ["crates/std/crates/backtrace-rs"]
[package] [package]
name = "kernel-rust" name = "kernel-rust"

View File

@@ -1,4 +1,5 @@
#![feature(iterator_try_collect, iter_order_by)] #![feature(iterator_try_collect, iter_order_by)]
#![allow(unused_features)]
#![cfg_attr(any(not(feature = "std"), target_arch = "riscv64"), no_std)] #![cfg_attr(any(not(feature = "std"), target_arch = "riscv64"), no_std)]
use core::cell::RefCell; use core::cell::RefCell;

View File

@@ -4,7 +4,8 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
backtrace_rs = { package = "backtrace", version = "0.3", default-features = false } cfg-if = { version = "1.0" }
rustc-demangle = { version = "0.1.27" }
hashbrown = "0.16" hashbrown = "0.16"
os-std-macros = { path = "../os-std-macros" } os-std-macros = { path = "../os-std-macros" }
shared = { path = "../shared", features = ["user"] } shared = { path = "../shared", features = ["user"] }

View File

@@ -28,17 +28,29 @@ cp_std path:
setup-std: setup-std:
@# Not copied : sys/mod.rs, io.rs, error.rs, thread.rs, sync.rs @# Not copied : sys/mod.rs, io.rs, error.rs, thread.rs, sync.rs
# @just cp_std "process.rs"
# @just cp_std "fs.rs"
# @just cp_std "collections/mod.rs" # @just cp_std "collections/mod.rs"
@just cp_std "alloc.rs"
@just cp_std "ascii.rs"
@just cp_std "backtrace.rs"
@just cp_std "bstr.rs"
@just cp_std "env.rs" @just cp_std "env.rs"
@just cp_std "error.rs"
@just cp_std "fs.rs"
@just cp_std "keyword_docs.rs"
@just cp_std "macros.rs"
@just cp_std "panic.rs"
@just cp_std "panicking.rs"
@just cp_std "pat.rs"
@just cp_std "path.rs"
@just cp_std "process.rs"
@just cp_std "random.rs"
@just cp_std "rt.rs"
# @just cp_std "tests_helpers.rs"
@just cp_std "time.rs" @just cp_std "time.rs"
@just cp_std "os/mod.rs" @just cp_std "os/mod.rs"
@just cp_std "os/raw/mod.rs" @just cp_std "os/raw/mod.rs"
@just cp_std "os/raw/tests.rs" @just cp_std "os/raw/tests.rs"
@just cp_std "bstr.rs"
@just cp_std "panicking.rs"
@just cp_std "panic.rs"
@just cp_std "collections/hash/map.rs" @just cp_std "collections/hash/map.rs"
@just cp_std "collections/hash/set.rs" @just cp_std "collections/hash/set.rs"
@just cp_std "collections/hash/mod.rs" @just cp_std "collections/hash/mod.rs"
@@ -114,6 +126,7 @@ setup-std:
@just cp_std "sys/cmath.rs" @just cp_std "sys/cmath.rs"
@just cp_std "sys/process/mod.rs" @just cp_std "sys/process/mod.rs"
@just cp_std "sys/process/env.rs" @just cp_std "sys/process/env.rs"
@just cp_std "sys/process/unsupported.rs"
@just cp_std "sys/args/mod.rs" @just cp_std "sys/args/mod.rs"
@just cp_std "sys/args/unsupported.rs" @just cp_std "sys/args/unsupported.rs"
@just cp_std "sys/pal/mod.rs" @just cp_std "sys/pal/mod.rs"
@@ -162,7 +175,10 @@ setup-std:
@just cp_std "sys/stdio/unsupported.rs" @just cp_std "sys/stdio/unsupported.rs"
@just cp_std "sys/alloc/mod.rs" @just cp_std "sys/alloc/mod.rs"
@just cp_std "sys/backtrace.rs" @just cp_std "sys/backtrace.rs"
@just cp_std "alloc.rs" @just cp_std "sys/fs/mod.rs"
@just cp_std "sys/fs/unsupported.rs"
@just cp_std "sys/helpers/mod.rs"
@just cp_std "sys/helpers/small_c_string.rs"
@just cp_std "sys/helpers/tests.rs"
@just cp_std "sys/helpers/wstr.rs"
@# Copied but edited for the moment @# Copied but edited for the moment
# @just cp_std "sys/process/unsupported.rs"
# @just cp_std "path.rs"

View File

@@ -1,5 +1,4 @@
s|crate::collections::TryReserveError|alloc_crate::collections::TryReserveError|g s|crate::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
s|crate::sync::Arc|alloc_crate::sync::Arc|g
s|alloc::ffi|alloc_crate::ffi|g s|alloc::ffi|alloc_crate::ffi|g
s|alloc::slice::Join|alloc_crate::slice::Join|g s|alloc::slice::Join|alloc_crate::slice::Join|g
s|alloc::bstr|alloc_crate::bstr|g s|alloc::bstr|alloc_crate::bstr|g

210
crates/std/src/ascii.rs Normal file
View File

@@ -0,0 +1,210 @@
//! Operations on ASCII strings and characters.
//!
//! Most string operations in Rust act on UTF-8 strings. However, at times it
//! makes more sense to only consider the ASCII character set for a specific
//! operation.
//!
//! The [`AsciiExt`] trait provides methods that allow for character
//! operations that only act on the ASCII subset and leave non-ASCII characters
//! alone.
//!
//! The [`escape_default`] function provides an iterator over the bytes of an
//! escaped version of the character given.
#![stable(feature = "rust1", since = "1.0.0")]
#[unstable(feature = "ascii_char", issue = "110998")]
pub use core::ascii::Char;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::ascii::{EscapeDefault, escape_default};
/// Extension methods for ASCII-subset only operations.
///
/// Be aware that operations on seemingly non-ASCII characters can sometimes
/// have unexpected results. Consider this example:
///
/// ```
/// use std::ascii::AsciiExt;
///
/// assert_eq!(AsciiExt::to_ascii_uppercase("café"), "CAFÉ");
/// assert_eq!(AsciiExt::to_ascii_uppercase("café"), "CAFé");
/// ```
///
/// In the first example, the lowercased string is represented `"cafe\u{301}"`
/// (the last character is an acute accent [combining character]). Unlike the
/// other characters in the string, the combining character will not get mapped
/// to an uppercase variant, resulting in `"CAFE\u{301}"`. In the second
/// example, the lowercased string is represented `"caf\u{e9}"` (the last
/// character is a single Unicode character representing an 'e' with an acute
/// accent). Since the last character is defined outside the scope of ASCII,
/// it will not get mapped to an uppercase variant, resulting in `"CAF\u{e9}"`.
///
/// [combining character]: https://en.wikipedia.org/wiki/Combining_character
#[stable(feature = "rust1", since = "1.0.0")]
#[deprecated(since = "1.26.0", note = "use inherent methods instead")]
pub trait AsciiExt {
/// Container type for copied ASCII characters.
#[stable(feature = "rust1", since = "1.0.0")]
type Owned;
/// Checks if the value is within the ASCII range.
///
/// # Note
///
/// This method is deprecated in favor of the identically-named
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
#[stable(feature = "rust1", since = "1.0.0")]
fn is_ascii(&self) -> bool;
/// Makes a copy of the value in its ASCII upper case equivalent.
///
/// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z',
/// but non-ASCII letters are unchanged.
///
/// To uppercase the value in-place, use [`make_ascii_uppercase`].
///
/// To uppercase ASCII characters in addition to non-ASCII characters, use
/// [`str::to_uppercase`].
///
/// # Note
///
/// This method is deprecated in favor of the identically-named
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
///
/// [`make_ascii_uppercase`]: AsciiExt::make_ascii_uppercase
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated)]
fn to_ascii_uppercase(&self) -> Self::Owned;
/// Makes a copy of the value in its ASCII lower case equivalent.
///
/// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z',
/// but non-ASCII letters are unchanged.
///
/// To lowercase the value in-place, use [`make_ascii_lowercase`].
///
/// To lowercase ASCII characters in addition to non-ASCII characters, use
/// [`str::to_lowercase`].
///
/// # Note
///
/// This method is deprecated in favor of the identically-named
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
///
/// [`make_ascii_lowercase`]: AsciiExt::make_ascii_lowercase
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated)]
fn to_ascii_lowercase(&self) -> Self::Owned;
/// Checks that two values are an ASCII case-insensitive match.
///
/// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`,
/// but without allocating and copying temporaries.
///
/// # Note
///
/// This method is deprecated in favor of the identically-named
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
#[stable(feature = "rust1", since = "1.0.0")]
fn eq_ignore_ascii_case(&self, other: &Self) -> bool;
/// Converts this type to its ASCII upper case equivalent in-place.
///
/// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z',
/// but non-ASCII letters are unchanged.
///
/// To return a new uppercased value without modifying the existing one, use
/// [`to_ascii_uppercase`].
///
/// # Note
///
/// This method is deprecated in favor of the identically-named
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
///
/// [`to_ascii_uppercase`]: AsciiExt::to_ascii_uppercase
#[stable(feature = "ascii", since = "1.9.0")]
fn make_ascii_uppercase(&mut self);
/// Converts this type to its ASCII lower case equivalent in-place.
///
/// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z',
/// but non-ASCII letters are unchanged.
///
/// To return a new lowercased value without modifying the existing one, use
/// [`to_ascii_lowercase`].
///
/// # Note
///
/// This method is deprecated in favor of the identically-named
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
///
/// [`to_ascii_lowercase`]: AsciiExt::to_ascii_lowercase
#[stable(feature = "ascii", since = "1.9.0")]
fn make_ascii_lowercase(&mut self);
}
macro_rules! delegating_ascii_methods {
() => {
#[inline]
fn is_ascii(&self) -> bool {
self.is_ascii()
}
#[inline]
fn to_ascii_uppercase(&self) -> Self::Owned {
self.to_ascii_uppercase()
}
#[inline]
fn to_ascii_lowercase(&self) -> Self::Owned {
self.to_ascii_lowercase()
}
#[inline]
fn eq_ignore_ascii_case(&self, o: &Self) -> bool {
self.eq_ignore_ascii_case(o)
}
#[inline]
fn make_ascii_uppercase(&mut self) {
self.make_ascii_uppercase();
}
#[inline]
fn make_ascii_lowercase(&mut self) {
self.make_ascii_lowercase();
}
};
}
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated)]
impl AsciiExt for u8 {
type Owned = u8;
delegating_ascii_methods!();
}
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated)]
impl AsciiExt for char {
type Owned = char;
delegating_ascii_methods!();
}
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated)]
impl AsciiExt for [u8] {
type Owned = Vec<u8>;
delegating_ascii_methods!();
}
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated)]
impl AsciiExt for str {
type Owned = String;
delegating_ascii_methods!();
}

478
crates/std/src/backtrace.rs Normal file
View File

@@ -0,0 +1,478 @@
//! Support for capturing a stack backtrace of an OS thread
//!
//! This module contains the support necessary to capture a stack backtrace of a
//! running OS thread from the OS thread itself. The `Backtrace` type supports
//! capturing a stack trace via the `Backtrace::capture` and
//! `Backtrace::force_capture` functions.
//!
//! A backtrace is typically quite handy to attach to errors (e.g. types
//! implementing `std::error::Error`) to get a causal chain of where an error
//! was generated.
//!
//! ## Accuracy
//!
//! Backtraces are attempted to be as accurate as possible, but no guarantees
//! are provided about the exact accuracy of a backtrace. Instruction pointers,
//! symbol names, filenames, line numbers, etc, may all be incorrect when
//! reported. Accuracy is attempted on a best-effort basis, however, any bug
//! reports are always welcome to indicate areas of improvement!
//!
//! For most platforms a backtrace with a filename/line number requires that
//! programs be compiled with debug information. Without debug information
//! filenames/line numbers will not be reported.
//!
//! ## Platform support
//!
//! Not all platforms that std compiles for support capturing backtraces. Some
//! platforms simply do nothing when capturing a backtrace. To check whether the
//! platform supports capturing backtraces you can consult the `BacktraceStatus`
//! enum as a result of `Backtrace::status`.
//!
//! Like above with accuracy platform support is done on a best effort basis.
//! Sometimes libraries might not be available at runtime or something may go
//! wrong which would cause a backtrace to not be captured. Please feel free to
//! report issues with platforms where a backtrace cannot be captured though!
//!
//! ## Environment Variables
//!
//! The `Backtrace::capture` function might not actually capture a backtrace by
//! default. Its behavior is governed by two environment variables:
//!
//! * `RUST_LIB_BACKTRACE` - if this is set to `0` then `Backtrace::capture`
//! will never capture a backtrace. Any other value set will enable
//! `Backtrace::capture`.
//!
//! * `RUST_BACKTRACE` - if `RUST_LIB_BACKTRACE` is not set, then this variable
//! is consulted with the same rules of `RUST_LIB_BACKTRACE`.
//!
//! * If neither of the above env vars are set, then `Backtrace::capture` will
//! be disabled.
//!
//! Capturing a backtrace can be a quite expensive runtime operation, so the
//! environment variables allow either forcibly disabling this runtime
//! performance hit or allow selectively enabling it in some programs.
//!
//! Note that the `Backtrace::force_capture` function can be used to ignore
//! these environment variables. Also note that the state of environment
//! variables is cached once the first backtrace is created, so altering
//! `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` at runtime might not actually change
//! how backtraces are captured.
#![stable(feature = "backtrace", since = "1.65.0")]
#[cfg(test)]
mod tests;
// NB: A note on resolution of a backtrace:
//
// Backtraces primarily happen in two steps, one is where we actually capture
// the stack backtrace, giving us a list of instruction pointers corresponding
// to stack frames. Next we take these instruction pointers and, one-by-one,
// turn them into a human readable name (like `main`).
//
// The first phase can be somewhat expensive (walking the stack), especially
// on MSVC where debug information is consulted to return inline frames each as
// their own frame. The second phase, however, is almost always extremely
// expensive (on the order of milliseconds sometimes) when it's consulting debug
// information.
//
// We attempt to amortize this cost as much as possible by delaying resolution
// of an address to a human readable name for as long as possible. When
// `Backtrace::create` is called to capture a backtrace it doesn't actually
// perform any symbol resolution, but rather we lazily resolve symbols only just
// before they're needed for printing. This way we can make capturing a
// backtrace and throwing it away much cheaper, but actually printing a
// backtrace is still basically the same cost.
//
// This strategy comes at the cost of some synchronization required inside of a
// `Backtrace`, but that's a relatively small price to pay relative to capturing
// a backtrace or actually symbolizing it.
use crate::backtrace_rs::{self, BytesOrWideString};
use crate::ffi::c_void;
use crate::panic::UnwindSafe;
use crate::sync::LazyLock;
use crate::sync::atomic::Ordering::Relaxed;
use crate::sync::atomic::{Atomic, AtomicU8};
use crate::sys::backtrace::{lock, output_filename, set_image_base};
use crate::{env, fmt};
/// A captured OS thread stack backtrace.
///
/// This type represents a stack backtrace for an OS thread captured at a
/// previous point in time. In some instances the `Backtrace` type may
/// internally be empty due to configuration. For more information see
/// `Backtrace::capture`.
#[stable(feature = "backtrace", since = "1.65.0")]
#[must_use]
pub struct Backtrace {
inner: Inner,
}
/// The current status of a backtrace, indicating whether it was captured or
/// whether it is empty for some other reason.
#[stable(feature = "backtrace", since = "1.65.0")]
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq)]
pub enum BacktraceStatus {
/// Capturing a backtrace is not supported, likely because it's not
/// implemented for the current platform.
#[stable(feature = "backtrace", since = "1.65.0")]
Unsupported,
/// Capturing a backtrace has been disabled through either the
/// `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` environment variables.
#[stable(feature = "backtrace", since = "1.65.0")]
Disabled,
/// A backtrace has been captured and the `Backtrace` should print
/// reasonable information when rendered.
#[stable(feature = "backtrace", since = "1.65.0")]
Captured,
}
enum Inner {
Unsupported,
Disabled,
Captured(LazyLock<Capture, LazyResolve>),
}
struct Capture {
actual_start: usize,
frames: Vec<BacktraceFrame>,
}
fn _assert_send_sync() {
fn _assert<T: Send + Sync>() {}
_assert::<Backtrace>();
}
/// A single frame of a backtrace.
#[unstable(feature = "backtrace_frames", issue = "79676")]
pub struct BacktraceFrame {
frame: RawFrame,
symbols: Vec<BacktraceSymbol>,
}
#[derive(Debug)]
enum RawFrame {
Actual(backtrace_rs::Frame),
#[cfg(test)]
Fake,
}
struct BacktraceSymbol {
name: Option<Vec<u8>>,
filename: Option<BytesOrWide>,
lineno: Option<u32>,
colno: Option<u32>,
}
enum BytesOrWide {
Bytes(Vec<u8>),
Wide(Vec<u16>),
}
#[stable(feature = "backtrace", since = "1.65.0")]
impl fmt::Debug for Backtrace {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let capture = match &self.inner {
Inner::Unsupported => return fmt.write_str("<unsupported>"),
Inner::Disabled => return fmt.write_str("<disabled>"),
Inner::Captured(c) => &**c,
};
let frames = &capture.frames[capture.actual_start..];
write!(fmt, "Backtrace ")?;
let mut dbg = fmt.debug_list();
for frame in frames {
if frame.frame.ip().is_null() {
continue;
}
dbg.entries(&frame.symbols);
}
dbg.finish()
}
}
#[unstable(feature = "backtrace_frames", issue = "79676")]
impl fmt::Debug for BacktraceFrame {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut dbg = fmt.debug_list();
dbg.entries(&self.symbols);
dbg.finish()
}
}
impl fmt::Debug for BacktraceSymbol {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
// FIXME: improve formatting: https://github.com/rust-lang/rust/issues/65280
// FIXME: Also, include column numbers into the debug format as Display already has them.
// Until there are stable per-frame accessors, the format shouldn't be changed:
// https://github.com/rust-lang/rust/issues/65280#issuecomment-638966585
write!(fmt, "{{ ")?;
if let Some(fn_name) = self.name.as_ref().map(|b| backtrace_rs::SymbolName::new(b)) {
write!(fmt, "fn: \"{:#}\"", fn_name)?;
} else {
write!(fmt, "fn: <unknown>")?;
}
if let Some(fname) = self.filename.as_ref() {
write!(fmt, ", file: \"{:?}\"", fname)?;
}
if let Some(line) = self.lineno {
write!(fmt, ", line: {:?}", line)?;
}
write!(fmt, " }}")
}
}
impl fmt::Debug for BytesOrWide {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
output_filename(
fmt,
match self {
BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w),
BytesOrWide::Wide(w) => BytesOrWideString::Wide(w),
},
backtrace_rs::PrintFmt::Short,
crate::env::current_dir().as_ref().ok(),
)
}
}
impl Backtrace {
/// Returns whether backtrace captures are enabled through environment
/// variables.
fn enabled() -> bool {
// Cache the result of reading the environment variables to make
// backtrace captures speedy, because otherwise reading environment
// variables every time can be somewhat slow.
static ENABLED: Atomic<u8> = AtomicU8::new(0);
match ENABLED.load(Relaxed) {
0 => {}
1 => return false,
_ => return true,
}
let enabled = match env::var("RUST_LIB_BACKTRACE") {
Ok(s) => s != "0",
Err(_) => match env::var("RUST_BACKTRACE") {
Ok(s) => s != "0",
Err(_) => false,
},
};
ENABLED.store(enabled as u8 + 1, Relaxed);
enabled
}
/// Captures a stack backtrace of the current thread.
///
/// This function will capture a stack backtrace of the current OS thread of
/// execution, returning a `Backtrace` type which can be later used to print
/// the entire stack trace or render it to a string.
///
/// This function will be a noop if the `RUST_BACKTRACE` or
/// `RUST_LIB_BACKTRACE` backtrace variables are both not set. If either
/// environment variable is set and enabled then this function will actually
/// capture a backtrace. Capturing a backtrace can be both memory intensive
/// and slow, so these environment variables allow liberally using
/// `Backtrace::capture` and only incurring a slowdown when the environment
/// variables are set.
///
/// To forcibly capture a backtrace regardless of environment variables, use
/// the `Backtrace::force_capture` function.
#[stable(feature = "backtrace", since = "1.65.0")]
#[inline(never)] // want to make sure there's a frame here to remove
pub fn capture() -> Backtrace {
if !Backtrace::enabled() {
return Backtrace { inner: Inner::Disabled };
}
Backtrace::create(Backtrace::capture as fn() -> Backtrace as usize)
}
/// Forcibly captures a full backtrace, regardless of environment variable
/// configuration.
///
/// This function behaves the same as `capture` except that it ignores the
/// values of the `RUST_BACKTRACE` and `RUST_LIB_BACKTRACE` environment
/// variables, always capturing a backtrace.
///
/// Note that capturing a backtrace can be an expensive operation on some
/// platforms, so this should be used with caution in performance-sensitive
/// parts of code.
#[stable(feature = "backtrace", since = "1.65.0")]
#[inline(never)] // want to make sure there's a frame here to remove
pub fn force_capture() -> Backtrace {
Backtrace::create(Backtrace::force_capture as fn() -> Backtrace as usize)
}
/// Forcibly captures a disabled backtrace, regardless of environment
/// variable configuration.
#[stable(feature = "backtrace", since = "1.65.0")]
#[rustc_const_stable(feature = "backtrace", since = "1.65.0")]
pub const fn disabled() -> Backtrace {
Backtrace { inner: Inner::Disabled }
}
// Capture a backtrace which start just before the function addressed by
// `ip`
fn create(ip: usize) -> Backtrace {
let _lock = lock();
let mut frames = Vec::new();
let mut actual_start = None;
set_image_base();
unsafe {
backtrace_rs::trace_unsynchronized(|frame| {
frames.push(BacktraceFrame {
frame: RawFrame::Actual(frame.clone()),
symbols: Vec::new(),
});
if frame.symbol_address().addr() == ip && actual_start.is_none() {
actual_start = Some(frames.len());
}
true
});
}
// If no frames came out assume that this is an unsupported platform
// since `backtrace` doesn't provide a way of learning this right now,
// and this should be a good enough approximation.
let inner = if frames.is_empty() {
Inner::Unsupported
} else {
Inner::Captured(LazyLock::new(lazy_resolve(Capture {
actual_start: actual_start.unwrap_or(0),
frames,
})))
};
Backtrace { inner }
}
/// Returns the status of this backtrace, indicating whether this backtrace
/// request was unsupported, disabled, or a stack trace was actually
/// captured.
#[stable(feature = "backtrace", since = "1.65.0")]
#[must_use]
pub fn status(&self) -> BacktraceStatus {
match self.inner {
Inner::Unsupported => BacktraceStatus::Unsupported,
Inner::Disabled => BacktraceStatus::Disabled,
Inner::Captured(_) => BacktraceStatus::Captured,
}
}
}
impl<'a> Backtrace {
/// Returns an iterator over the backtrace frames.
#[must_use]
#[unstable(feature = "backtrace_frames", issue = "79676")]
pub fn frames(&'a self) -> &'a [BacktraceFrame] {
if let Inner::Captured(c) = &self.inner { &c.frames } else { &[] }
}
}
#[stable(feature = "backtrace", since = "1.65.0")]
impl fmt::Display for Backtrace {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let capture = match &self.inner {
Inner::Unsupported => return fmt.write_str("unsupported backtrace"),
Inner::Disabled => return fmt.write_str("disabled backtrace"),
Inner::Captured(c) => &**c,
};
let full = fmt.alternate();
let (frames, style) = if full {
(&capture.frames[..], backtrace_rs::PrintFmt::Full)
} else {
(&capture.frames[capture.actual_start..], backtrace_rs::PrintFmt::Short)
};
// When printing paths we try to strip the cwd if it exists, otherwise
// we just print the path as-is. Note that we also only do this for the
// short format, because if it's full we presumably want to print
// everything.
let cwd = crate::env::current_dir();
let mut print_path = move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
output_filename(fmt, path, style, cwd.as_ref().ok())
};
let mut f = backtrace_rs::BacktraceFmt::new(fmt, style, &mut print_path);
f.add_context()?;
for frame in frames {
if frame.symbols.is_empty() {
f.frame().print_raw(frame.frame.ip(), None, None, None)?;
} else {
for symbol in frame.symbols.iter() {
f.frame().print_raw_with_column(
frame.frame.ip(),
symbol.name.as_ref().map(|b| backtrace_rs::SymbolName::new(b)),
symbol.filename.as_ref().map(|b| match b {
BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w),
BytesOrWide::Wide(w) => BytesOrWideString::Wide(w),
}),
symbol.lineno,
symbol.colno,
)?;
}
}
}
f.finish()?;
Ok(())
}
}
mod helper {
use super::*;
pub(super) type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync + UnwindSafe;
#[define_opaque(LazyResolve)]
pub(super) fn lazy_resolve(mut capture: Capture) -> LazyResolve {
move || {
// Use the global backtrace lock to synchronize this as it's a
// requirement of the `backtrace` crate, and then actually resolve
// everything.
let _lock = lock();
for frame in capture.frames.iter_mut() {
let symbols = &mut frame.symbols;
let frame = match &frame.frame {
RawFrame::Actual(frame) => frame,
#[cfg(test)]
RawFrame::Fake => unimplemented!(),
};
unsafe {
backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
symbols.push(BacktraceSymbol {
name: symbol.name().map(|m| m.as_bytes().to_vec()),
filename: symbol.filename_raw().map(|b| match b {
BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()),
BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()),
}),
lineno: symbol.lineno(),
colno: symbol.colno(),
});
});
}
}
capture
}
}
}
use helper::*;
impl RawFrame {
fn ip(&self) -> *mut c_void {
match self {
RawFrame::Actual(frame) => frame.ip(),
#[cfg(test)]
RawFrame::Fake => crate::ptr::without_provenance_mut(1),
}
}
}

View File

@@ -357,6 +357,7 @@ impl<K, V, S> HashMap<K, V, S> {
/// map.insert(1, 2); /// map.insert(1, 2);
/// ``` /// ```
#[inline] #[inline]
#[must_use]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
#[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")]
pub const fn with_hasher(hash_builder: S) -> HashMap<K, V, S> { pub const fn with_hasher(hash_builder: S) -> HashMap<K, V, S> {
@@ -389,6 +390,7 @@ impl<K, V, S> HashMap<K, V, S> {
/// map.insert(1, 2); /// map.insert(1, 2);
/// ``` /// ```
#[inline] #[inline]
#[must_use]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashMap<K, V, S> { pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashMap<K, V, S> {
HashMap { base: base::HashMap::with_capacity_and_hasher(capacity, hasher) } HashMap { base: base::HashMap::with_capacity_and_hasher(capacity, hasher) }
@@ -409,6 +411,7 @@ impl<K, V, S, A: Allocator> HashMap<K, V, S, A> {
/// The `hash_builder` passed should implement the [`BuildHasher`] trait for /// The `hash_builder` passed should implement the [`BuildHasher`] trait for
/// the `HashMap` to be useful, see its documentation for details. /// the `HashMap` to be useful, see its documentation for details.
#[inline] #[inline]
#[must_use]
#[unstable(feature = "allocator_api", issue = "32838")] #[unstable(feature = "allocator_api", issue = "32838")]
pub fn with_hasher_in(hash_builder: S, alloc: A) -> Self { pub fn with_hasher_in(hash_builder: S, alloc: A) -> Self {
HashMap { base: base::HashMap::with_hasher_in(hash_builder, alloc) } HashMap { base: base::HashMap::with_hasher_in(hash_builder, alloc) }
@@ -430,6 +433,7 @@ impl<K, V, S, A: Allocator> HashMap<K, V, S, A> {
/// the `HashMap` to be useful, see its documentation for details. /// the `HashMap` to be useful, see its documentation for details.
/// ///
#[inline] #[inline]
#[must_use]
#[unstable(feature = "allocator_api", issue = "32838")] #[unstable(feature = "allocator_api", issue = "32838")]
pub fn with_capacity_and_hasher_in(capacity: usize, hash_builder: S, alloc: A) -> Self { pub fn with_capacity_and_hasher_in(capacity: usize, hash_builder: S, alloc: A) -> Self {
HashMap { base: base::HashMap::with_capacity_and_hasher_in(capacity, hash_builder, alloc) } HashMap { base: base::HashMap::with_capacity_and_hasher_in(capacity, hash_builder, alloc) }

View File

@@ -229,6 +229,7 @@ impl<T, S> HashSet<T, S> {
/// set.insert(2); /// set.insert(2);
/// ``` /// ```
#[inline] #[inline]
#[must_use]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
#[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")]
pub const fn with_hasher(hasher: S) -> HashSet<T, S> { pub const fn with_hasher(hasher: S) -> HashSet<T, S> {
@@ -261,6 +262,7 @@ impl<T, S> HashSet<T, S> {
/// set.insert(1); /// set.insert(1);
/// ``` /// ```
#[inline] #[inline]
#[must_use]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet<T, S> { pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet<T, S> {
HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, hasher) } HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, hasher) }
@@ -281,6 +283,7 @@ impl<T, S, A: Allocator> HashSet<T, S, A> {
/// The `hash_builder` passed should implement the [`BuildHasher`] trait for /// The `hash_builder` passed should implement the [`BuildHasher`] trait for
/// the `HashSet` to be useful, see its documentation for details. /// the `HashSet` to be useful, see its documentation for details.
#[inline] #[inline]
#[must_use]
#[unstable(feature = "allocator_api", issue = "32838")] #[unstable(feature = "allocator_api", issue = "32838")]
pub fn with_hasher_in(hasher: S, alloc: A) -> HashSet<T, S, A> { pub fn with_hasher_in(hasher: S, alloc: A) -> HashSet<T, S, A> {
HashSet { base: base::HashSet::with_hasher_in(hasher, alloc) } HashSet { base: base::HashSet::with_hasher_in(hasher, alloc) }
@@ -301,6 +304,7 @@ impl<T, S, A: Allocator> HashSet<T, S, A> {
/// The `hash_builder` passed should implement the [`BuildHasher`] trait for /// The `hash_builder` passed should implement the [`BuildHasher`] trait for
/// the `HashSet` to be useful, see its documentation for details. /// the `HashSet` to be useful, see its documentation for details.
#[inline] #[inline]
#[must_use]
#[unstable(feature = "allocator_api", issue = "32838")] #[unstable(feature = "allocator_api", issue = "32838")]
pub fn with_capacity_and_hasher_in(capacity: usize, hasher: S, alloc: A) -> HashSet<T, S, A> { pub fn with_capacity_and_hasher_in(capacity: usize, hasher: S, alloc: A) -> HashSet<T, S, A> {
HashSet { base: base::HashSet::with_capacity_and_hasher_in(capacity, hasher, alloc) } HashSet { base: base::HashSet::with_capacity_and_hasher_in(capacity, hasher, alloc) }

View File

@@ -670,6 +670,17 @@ pub fn home_dir() -> Option<PathBuf> {
/// ///
/// On Windows, the behavior is equivalent to that of [`GetTempPath2`][GetTempPath2] / /// On Windows, the behavior is equivalent to that of [`GetTempPath2`][GetTempPath2] /
/// [`GetTempPath`][GetTempPath], which this function uses internally. /// [`GetTempPath`][GetTempPath], which this function uses internally.
/// Specifically, for non-SYSTEM processes, the function checks for the
/// following environment variables in order and returns the first path found:
///
/// 1. The path specified by the `TMP` environment variable.
/// 2. The path specified by the `TEMP` environment variable.
/// 3. The path specified by the `USERPROFILE` environment variable.
/// 4. The Windows directory.
///
/// When called from a process running as SYSTEM,
/// [`GetTempPath2`][GetTempPath2] returns `C:\Windows\SystemTemp`
/// regardless of environment variables.
/// ///
/// Note that, this [may change in the future][changes]. /// Note that, this [may change in the future][changes].
/// ///

View File

@@ -1,4 +1,562 @@
// todo retreive docs
#![stable(feature = "rust1", since = "1.0.0")]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub use core::error::Error; pub use core::error::Error;
#[unstable(feature = "error_generic_member_access", issue = "99301")] #[unstable(feature = "error_generic_member_access", issue = "99301")]
pub use core::error::{Request, request_ref, request_value}; pub use core::error::{Request, request_ref, request_value};
use crate::backtrace::Backtrace;
use crate::fmt::{self, Write};
/// An error reporter that prints an error and its sources.
///
/// Report also exposes configuration options for formatting the error sources, either entirely on a
/// single line, or in multi-line format with each source on a new line.
///
/// `Report` only requires that the wrapped error implement `Error`. It doesn't require that the
/// wrapped error be `Send`, `Sync`, or `'static`.
///
/// # Examples
///
/// ```rust
/// #![feature(error_reporter)]
/// use std::error::{Error, Report};
/// use std::fmt;
///
/// #[derive(Debug)]
/// struct SuperError {
/// source: SuperErrorSideKick,
/// }
///
/// impl fmt::Display for SuperError {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "SuperError is here!")
/// }
/// }
///
/// impl Error for SuperError {
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
/// Some(&self.source)
/// }
/// }
///
/// #[derive(Debug)]
/// struct SuperErrorSideKick;
///
/// impl fmt::Display for SuperErrorSideKick {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "SuperErrorSideKick is here!")
/// }
/// }
///
/// impl Error for SuperErrorSideKick {}
///
/// fn get_super_error() -> Result<(), SuperError> {
/// Err(SuperError { source: SuperErrorSideKick })
/// }
///
/// fn main() {
/// match get_super_error() {
/// Err(e) => println!("Error: {}", Report::new(e)),
/// _ => println!("No error"),
/// }
/// }
/// ```
///
/// This example produces the following output:
///
/// ```console
/// Error: SuperError is here!: SuperErrorSideKick is here!
/// ```
///
/// ## Output consistency
///
/// Report prints the same output via `Display` and `Debug`, so it works well with
/// [`Result::unwrap`]/[`Result::expect`] which print their `Err` variant via `Debug`:
///
/// ```should_panic
/// #![feature(error_reporter)]
/// use std::error::Report;
/// # use std::error::Error;
/// # use std::fmt;
/// # #[derive(Debug)]
/// # struct SuperError {
/// # source: SuperErrorSideKick,
/// # }
/// # impl fmt::Display for SuperError {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "SuperError is here!")
/// # }
/// # }
/// # impl Error for SuperError {
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
/// # Some(&self.source)
/// # }
/// # }
/// # #[derive(Debug)]
/// # struct SuperErrorSideKick;
/// # impl fmt::Display for SuperErrorSideKick {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "SuperErrorSideKick is here!")
/// # }
/// # }
/// # impl Error for SuperErrorSideKick {}
/// # fn get_super_error() -> Result<(), SuperError> {
/// # Err(SuperError { source: SuperErrorSideKick })
/// # }
///
/// get_super_error().map_err(Report::new).unwrap();
/// ```
///
/// This example produces the following output:
///
/// ```console
/// thread 'main' panicked at src/error.rs:34:40:
/// called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!
/// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
/// ```
///
/// ## Return from `main`
///
/// `Report` also implements `From` for all types that implement [`Error`]; this when combined with
/// the `Debug` output means `Report` is an ideal starting place for formatting errors returned
/// from `main`.
///
/// ```should_panic
/// #![feature(error_reporter)]
/// use std::error::Report;
/// # use std::error::Error;
/// # use std::fmt;
/// # #[derive(Debug)]
/// # struct SuperError {
/// # source: SuperErrorSideKick,
/// # }
/// # impl fmt::Display for SuperError {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "SuperError is here!")
/// # }
/// # }
/// # impl Error for SuperError {
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
/// # Some(&self.source)
/// # }
/// # }
/// # #[derive(Debug)]
/// # struct SuperErrorSideKick;
/// # impl fmt::Display for SuperErrorSideKick {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "SuperErrorSideKick is here!")
/// # }
/// # }
/// # impl Error for SuperErrorSideKick {}
/// # fn get_super_error() -> Result<(), SuperError> {
/// # Err(SuperError { source: SuperErrorSideKick })
/// # }
///
/// fn main() -> Result<(), Report<SuperError>> {
/// get_super_error()?;
/// Ok(())
/// }
/// ```
///
/// This example produces the following output:
///
/// ```console
/// Error: SuperError is here!: SuperErrorSideKick is here!
/// ```
///
/// **Note**: `Report`s constructed via `?` and `From` will be configured to use the single line
/// output format. If you want to make sure your `Report`s are pretty printed and include backtrace
/// you will need to manually convert and enable those flags.
///
/// ```should_panic
/// #![feature(error_reporter)]
/// use std::error::Report;
/// # use std::error::Error;
/// # use std::fmt;
/// # #[derive(Debug)]
/// # struct SuperError {
/// # source: SuperErrorSideKick,
/// # }
/// # impl fmt::Display for SuperError {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "SuperError is here!")
/// # }
/// # }
/// # impl Error for SuperError {
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
/// # Some(&self.source)
/// # }
/// # }
/// # #[derive(Debug)]
/// # struct SuperErrorSideKick;
/// # impl fmt::Display for SuperErrorSideKick {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "SuperErrorSideKick is here!")
/// # }
/// # }
/// # impl Error for SuperErrorSideKick {}
/// # fn get_super_error() -> Result<(), SuperError> {
/// # Err(SuperError { source: SuperErrorSideKick })
/// # }
///
/// fn main() -> Result<(), Report<SuperError>> {
/// get_super_error()
/// .map_err(Report::from)
/// .map_err(|r| r.pretty(true).show_backtrace(true))?;
/// Ok(())
/// }
/// ```
///
/// This example produces the following output:
///
/// ```console
/// Error: SuperError is here!
///
/// Caused by:
/// SuperErrorSideKick is here!
/// ```
#[unstable(feature = "error_reporter", issue = "90172")]
pub struct Report<E = Box<dyn Error>> {
/// The error being reported.
error: E,
/// Whether a backtrace should be included as part of the report.
show_backtrace: bool,
/// Whether the report should be pretty-printed.
pretty: bool,
}
impl<E> Report<E>
where
Report<E>: From<E>,
{
/// Creates a new `Report` from an input error.
#[unstable(feature = "error_reporter", issue = "90172")]
pub fn new(error: E) -> Report<E> {
Self::from(error)
}
}
impl<E> Report<E> {
/// Enable pretty-printing the report across multiple lines.
///
/// # Examples
///
/// ```rust
/// #![feature(error_reporter)]
/// use std::error::Report;
/// # use std::error::Error;
/// # use std::fmt;
/// # #[derive(Debug)]
/// # struct SuperError {
/// # source: SuperErrorSideKick,
/// # }
/// # impl fmt::Display for SuperError {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "SuperError is here!")
/// # }
/// # }
/// # impl Error for SuperError {
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
/// # Some(&self.source)
/// # }
/// # }
/// # #[derive(Debug)]
/// # struct SuperErrorSideKick;
/// # impl fmt::Display for SuperErrorSideKick {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "SuperErrorSideKick is here!")
/// # }
/// # }
/// # impl Error for SuperErrorSideKick {}
///
/// let error = SuperError { source: SuperErrorSideKick };
/// let report = Report::new(error).pretty(true);
/// eprintln!("Error: {report:?}");
/// ```
///
/// This example produces the following output:
///
/// ```console
/// Error: SuperError is here!
///
/// Caused by:
/// SuperErrorSideKick is here!
/// ```
///
/// When there are multiple source errors the causes will be numbered in order of iteration
/// starting from the outermost error.
///
/// ```rust
/// #![feature(error_reporter)]
/// use std::error::Report;
/// # use std::error::Error;
/// # use std::fmt;
/// # #[derive(Debug)]
/// # struct SuperError {
/// # source: SuperErrorSideKick,
/// # }
/// # impl fmt::Display for SuperError {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "SuperError is here!")
/// # }
/// # }
/// # impl Error for SuperError {
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
/// # Some(&self.source)
/// # }
/// # }
/// # #[derive(Debug)]
/// # struct SuperErrorSideKick {
/// # source: SuperErrorSideKickSideKick,
/// # }
/// # impl fmt::Display for SuperErrorSideKick {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "SuperErrorSideKick is here!")
/// # }
/// # }
/// # impl Error for SuperErrorSideKick {
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
/// # Some(&self.source)
/// # }
/// # }
/// # #[derive(Debug)]
/// # struct SuperErrorSideKickSideKick;
/// # impl fmt::Display for SuperErrorSideKickSideKick {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "SuperErrorSideKickSideKick is here!")
/// # }
/// # }
/// # impl Error for SuperErrorSideKickSideKick { }
///
/// let source = SuperErrorSideKickSideKick;
/// let source = SuperErrorSideKick { source };
/// let error = SuperError { source };
/// let report = Report::new(error).pretty(true);
/// eprintln!("Error: {report:?}");
/// ```
///
/// This example produces the following output:
///
/// ```console
/// Error: SuperError is here!
///
/// Caused by:
/// 0: SuperErrorSideKick is here!
/// 1: SuperErrorSideKickSideKick is here!
/// ```
#[unstable(feature = "error_reporter", issue = "90172")]
pub fn pretty(mut self, pretty: bool) -> Self {
self.pretty = pretty;
self
}
/// Display backtrace if available when using pretty output format.
///
/// # Examples
///
/// **Note**: Report will search for the first `Backtrace` it can find starting from the
/// outermost error. In this example it will display the backtrace from the second error in the
/// sources, `SuperErrorSideKick`.
///
/// ```rust
/// #![feature(error_reporter)]
/// #![feature(error_generic_member_access)]
/// # use std::error::Error;
/// # use std::fmt;
/// use std::error::Request;
/// use std::error::Report;
/// use std::backtrace::Backtrace;
///
/// # #[derive(Debug)]
/// # struct SuperError {
/// # source: SuperErrorSideKick,
/// # }
/// # impl fmt::Display for SuperError {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "SuperError is here!")
/// # }
/// # }
/// # impl Error for SuperError {
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
/// # Some(&self.source)
/// # }
/// # }
/// #[derive(Debug)]
/// struct SuperErrorSideKick {
/// backtrace: Backtrace,
/// }
///
/// impl SuperErrorSideKick {
/// fn new() -> SuperErrorSideKick {
/// SuperErrorSideKick { backtrace: Backtrace::force_capture() }
/// }
/// }
///
/// impl Error for SuperErrorSideKick {
/// fn provide<'a>(&'a self, request: &mut Request<'a>) {
/// request.provide_ref::<Backtrace>(&self.backtrace);
/// }
/// }
///
/// // The rest of the example is unchanged ...
/// # impl fmt::Display for SuperErrorSideKick {
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// # write!(f, "SuperErrorSideKick is here!")
/// # }
/// # }
///
/// let source = SuperErrorSideKick::new();
/// let error = SuperError { source };
/// let report = Report::new(error).pretty(true).show_backtrace(true);
/// eprintln!("Error: {report:?}");
/// ```
///
/// This example produces something similar to the following output:
///
/// ```console
/// Error: SuperError is here!
///
/// Caused by:
/// SuperErrorSideKick is here!
///
/// Stack backtrace:
/// 0: rust_out::main::_doctest_main_src_error_rs_1158_0::SuperErrorSideKick::new
/// 1: rust_out::main::_doctest_main_src_error_rs_1158_0
/// 2: rust_out::main
/// 3: core::ops::function::FnOnce::call_once
/// 4: std::sys::backtrace::__rust_begin_short_backtrace
/// 5: std::rt::lang_start::{{closure}}
/// 6: std::panicking::try
/// 7: std::rt::lang_start_internal
/// 8: std::rt::lang_start
/// 9: main
/// 10: __libc_start_main
/// 11: _start
/// ```
#[unstable(feature = "error_reporter", issue = "90172")]
pub fn show_backtrace(mut self, show_backtrace: bool) -> Self {
self.show_backtrace = show_backtrace;
self
}
}
impl<E> Report<E>
where
E: Error,
{
fn backtrace(&self) -> Option<&Backtrace> {
// have to grab the backtrace on the first error directly since that error may not be
// 'static
let backtrace = request_ref(&self.error);
let backtrace = backtrace.or_else(|| {
self.error
.source()
.map(|source| source.sources().find_map(|source| request_ref(source)))
.flatten()
});
backtrace
}
/// Format the report as a single line.
#[unstable(feature = "error_reporter", issue = "90172")]
fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.error)?;
let sources = self.error.source().into_iter().flat_map(<dyn Error>::sources);
for cause in sources {
write!(f, ": {cause}")?;
}
Ok(())
}
/// Format the report as multiple lines, with each error cause on its own line.
#[unstable(feature = "error_reporter", issue = "90172")]
fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let error = &self.error;
write!(f, "{error}")?;
if let Some(cause) = error.source() {
write!(f, "\n\nCaused by:")?;
let multiple = cause.source().is_some();
for (ind, error) in cause.sources().enumerate() {
writeln!(f)?;
let mut indented = Indented { inner: f };
if multiple {
write!(indented, "{ind: >4}: {error}")?;
} else {
write!(indented, " {error}")?;
}
}
}
if self.show_backtrace {
if let Some(backtrace) = self.backtrace() {
write!(f, "\n\nStack backtrace:\n{}", backtrace.to_string().trim_end())?;
}
}
Ok(())
}
}
#[unstable(feature = "error_reporter", issue = "90172")]
impl<E> From<E> for Report<E>
where
E: Error,
{
fn from(error: E) -> Self {
Report { error, show_backtrace: false, pretty: false }
}
}
#[unstable(feature = "error_reporter", issue = "90172")]
impl<E> fmt::Display for Report<E>
where
E: Error,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) }
}
}
// This type intentionally outputs the same format for `Display` and `Debug`for
// situations where you unwrap a `Report` or return it from main.
#[unstable(feature = "error_reporter", issue = "90172")]
impl<E> fmt::Debug for Report<E>
where
Report<E>: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
/// Wrapper type for indenting the inner source.
struct Indented<'a, D> {
inner: &'a mut D,
}
impl<T> Write for Indented<'_, T>
where
T: Write,
{
fn write_str(&mut self, s: &str) -> fmt::Result {
for (i, line) in s.split('\n').enumerate() {
if i > 0 {
self.inner.write_char('\n')?;
self.inner.write_str(" ")?;
}
self.inner.write_str(line)?;
}
Ok(())
}
}

View File

@@ -11,7 +11,7 @@ use crate::hash::{Hash, Hasher};
use crate::ops::{self, Range}; use crate::ops::{self, Range};
use crate::rc::Rc; use crate::rc::Rc;
use crate::str::FromStr; use crate::str::FromStr;
use alloc_crate::sync::Arc; use crate::sync::Arc;
use crate::sys::os_str::{Buf, Slice}; use crate::sys::os_str::{Buf, Slice};
use crate::sys::{AsInner, FromInner, IntoInner}; use crate::sys::{AsInner, FromInner, IntoInner};
use crate::{cmp, fmt, slice}; use crate::{cmp, fmt, slice};
@@ -576,15 +576,21 @@ impl OsString {
/// Truncate the `OsString` to the specified length. /// Truncate the `OsString` to the specified length.
/// ///
/// If `new_len` is greater than the string's current length, this has no
/// effect.
///
/// # Panics /// # Panics
///
/// Panics if `len` does not lie on a valid `OsStr` boundary /// Panics if `len` does not lie on a valid `OsStr` boundary
/// (as described in [`OsStr::slice_encoded_bytes`]). /// (as described in [`OsStr::slice_encoded_bytes`]).
#[inline] #[inline]
#[unstable(feature = "os_string_truncate", issue = "133262")] #[unstable(feature = "os_string_truncate", issue = "133262")]
pub fn truncate(&mut self, len: usize) { pub fn truncate(&mut self, len: usize) {
self.as_os_str().inner.check_public_boundary(len); if len <= self.len() {
// SAFETY: The length was just checked to be at a valid boundary. self.as_os_str().inner.check_public_boundary(len);
unsafe { self.inner.truncate_unchecked(len) }; // SAFETY: The length was just checked to be at a valid boundary.
unsafe { self.inner.truncate_unchecked(len) };
}
} }
/// Provides plumbing to `Vec::extend_from_slice` without giving full /// Provides plumbing to `Vec::extend_from_slice` without giving full

View File

@@ -36,7 +36,6 @@
target_os = "wasi", target_os = "wasi",
target_env = "sgx", target_env = "sgx",
target_os = "xous", target_os = "xous",
target_os = "survos",
target_os = "trusty", target_os = "trusty",
)) ))
))] ))]
@@ -46,7 +45,7 @@ use crate::ffi::OsString;
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
use crate::path::{Path, PathBuf}; use crate::path::{Path, PathBuf};
use crate::sealed::Sealed; use crate::sealed::Sealed;
use alloc::sync::Arc; use crate::sync::Arc;
use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, fs as fs_imp}; use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, fs as fs_imp};
use crate::time::SystemTime; use crate::time::SystemTime;
use crate::{error, fmt}; use crate::{error, fmt};
@@ -2681,7 +2680,7 @@ impl AsInner<fs_imp::DirEntry> for DirEntry {
/// ///
/// This function will only ever return an error of kind `NotFound` if the given /// This function will only ever return an error of kind `NotFound` if the given
/// path does not exist. Note that the inverse is not true, /// path does not exist. Note that the inverse is not true,
/// ie. if a path does not exist, its removal may fail for a number of reasons, /// i.e. if a path does not exist, its removal may fail for a number of reasons,
/// such as insufficient permissions. /// such as insufficient permissions.
/// ///
/// # Examples /// # Examples
@@ -3151,7 +3150,7 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
/// ///
/// This function will only ever return an error of kind `NotFound` if the given /// This function will only ever return an error of kind `NotFound` if the given
/// path does not exist. Note that the inverse is not true, /// path does not exist. Note that the inverse is not true,
/// ie. if a path does not exist, its removal may fail for a number of reasons, /// i.e. if a path does not exist, its removal may fail for a number of reasons,
/// such as insufficient permissions. /// such as insufficient permissions.
/// ///
/// # Examples /// # Examples

View File

@@ -21,12 +21,14 @@ pub use core::io::{BorrowedBuf, BorrowedCursor};
use core::slice::memchr; use core::slice::memchr;
pub use cursor::Cursor; pub use cursor::Cursor;
pub use self::stdio::{ pub use self::copy::copy;
Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout,
};
pub use self::pipe::{PipeReader, PipeWriter}; pub use self::pipe::{PipeReader, PipeWriter};
pub use stdio::try_set_output_capture; pub use self::stdio::cleanup;
pub use self::stdio::{
_print, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout,
};
pub(crate) use stdio::attempt_print_to_stderr; pub(crate) use stdio::attempt_print_to_stderr;
pub use stdio::try_set_output_capture;
use crate::fs::File; use crate::fs::File;
use io::IoBase; use io::IoBase;
@@ -43,7 +45,7 @@ impl IoBase for Stdin {
impl io::Read for Stdin { impl io::Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> { fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
unsafe { File::from_raw_fd(0).read(buf) } unsafe { crate::other_fs::File::from_raw_fd(0).read(buf) }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@
#![allow(unused_lifetimes)] #![allow(unused_lifetimes)]
#![allow(internal_features)] #![allow(internal_features)]
#![deny(fuzzy_provenance_casts)] #![deny(fuzzy_provenance_casts)]
#![allow(fuzzy_provenance_casts)]
#![deny(unsafe_op_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)]
#![allow(rustdoc::redundant_explicit_links)] #![allow(rustdoc::redundant_explicit_links)]
#![warn(rustdoc::unescaped_backticks)] #![warn(rustdoc::unescaped_backticks)]
@@ -265,7 +266,7 @@ pub use alloc_crate::vec;
pub use core::{ pub use core::{
assert, cfg, column, compile_error, concat, const_format_args, env, file, format_args, assert, cfg, column, compile_error, concat, const_format_args, env, file, format_args,
format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env,
stringify, trace_macros, stringify, trace_macros, unimplemented
}; };
#[rustc_std_internal_symbol] #[rustc_std_internal_symbol]
@@ -276,7 +277,6 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn core::panic::PanicPayload) -
pub mod ffi; pub mod ffi;
pub mod hash; pub mod hash;
pub mod io; pub mod io;
// pub mod fs;
pub mod error; pub mod error;
pub mod num; pub mod num;
pub mod path; pub mod path;
@@ -285,17 +285,25 @@ pub mod process;
#[macro_use] #[macro_use]
pub mod rt; pub mod rt;
pub mod alloc; pub mod alloc;
pub mod ascii;
pub mod backtrace;
pub mod bstr; pub mod bstr;
pub mod collections; pub mod collections;
pub mod env; pub mod env;
pub mod fs;
pub mod keyword_docs;
pub mod macros;
pub mod os; pub mod os;
pub mod panic; pub mod panic;
pub mod panicking; pub mod panicking;
pub mod pat;
pub mod sync; pub mod sync;
pub mod sys; pub mod sys;
pub mod thread; pub mod thread;
pub mod time; pub mod time;
pub use backtrace_rs;
#[path = "../crates/backtrace-rs/src/lib.rs"]
mod backtrace_rs;
#[prelude_import] #[prelude_import]
#[allow(unused_imports)] #[allow(unused_imports)]
@@ -310,23 +318,23 @@ mod sealed {
pub trait Sealed {} pub trait Sealed {}
} }
pub use shared::fs; pub use shared::fs as other_fs;
pub use shared::syscall; pub use shared::syscall;
#[macro_export] // #[macro_export]
macro_rules! print { // macro_rules! print {
($($args:expr),*) => { // ($($args:expr),*) => {
$crate::syscall::write_string_temp(&format!($($args),*)) // $crate::syscall::write_string_temp(&format!($($args),*))
}; // };
} // }
#[macro_export] // #[macro_export]
macro_rules! println { // macro_rules! println {
() => { // () => {
$crate::print!(""); // $crate::print!("");
// $crate::print!("\n\r"); // // $crate::print!("\n\r");
}; // };
($($args:expr),*) => { // ($($args:expr),*) => {
$crate::print!($($args),*); // $crate::print!($($args),*);
// $crate::println!(); // // $crate::println!();
}; // };
} // }

416
crates/std/src/macros.rs Normal file
View File

@@ -0,0 +1,416 @@
//! Standard library macros
//!
//! This module contains a set of macros which are exported from the standard
//! library. Each macro is available for use when linking against the standard
//! library.
// ignore-tidy-dbg
// todo retreive docs
#[macro_export]
#[rustc_builtin_macro(std_panic)]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable(edition_panic)]
#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")]
macro_rules! panic {
// Expands to either `$crate::panic::panic_2015` or `$crate::panic::panic_2021`
// depending on the edition of the caller.
($($arg:tt)*) => {
/* compiler built-in */
};
}
/// Prints to the standard output.
///
/// Equivalent to the [`println!`] macro except that a newline is not printed at
/// the end of the message.
///
/// Note that stdout is frequently line-buffered by default so it may be
/// necessary to use [`io::stdout().flush()`][flush] to ensure the output is emitted
/// immediately.
///
/// The `print!` macro will lock the standard output on each call. If you call
/// `print!` within a hot loop, this behavior may be the bottleneck of the loop.
/// To avoid this, lock stdout with [`io::stdout().lock()`][lock]:
/// ```
/// use std::io::{stdout, Write};
///
/// let mut lock = stdout().lock();
/// write!(lock, "hello world").unwrap();
/// ```
///
/// Use `print!` only for the primary output of your program. Use
/// [`eprint!`] instead to print error and progress messages.
///
/// See the formatting documentation in [`std::fmt`](crate::fmt)
/// for details of the macro argument syntax.
///
/// [flush]: crate::io::Write::flush
/// [`println!`]: crate::println
/// [`eprint!`]: crate::eprint
/// [lock]: crate::io::Stdout
///
/// # Panics
///
/// Panics if writing to `io::stdout()` fails.
///
/// Writing to non-blocking stdout can cause an error, which will lead
/// this macro to panic.
///
/// # Examples
///
/// ```
/// use std::io::{self, Write};
///
/// print!("this ");
/// print!("will ");
/// print!("be ");
/// print!("on ");
/// print!("the ");
/// print!("same ");
/// print!("line ");
///
/// io::stdout().flush().unwrap();
///
/// print!("this string has a newline, why not choose println! instead?\n");
///
/// io::stdout().flush().unwrap();
/// ```
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "print_macro")]
#[allow_internal_unstable(print_internals)]
macro_rules! print {
($($arg:tt)*) => {{
$crate::io::_print($crate::format_args!($($arg)*));
}};
}
/// Prints to the standard output, with a newline.
///
/// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone
/// (no additional CARRIAGE RETURN (`\r`/`U+000D`)).
///
/// This macro uses the same syntax as [`format!`], but writes to the standard output instead.
/// See [`std::fmt`] for more information.
///
/// The `println!` macro will lock the standard output on each call. If you call
/// `println!` within a hot loop, this behavior may be the bottleneck of the loop.
/// To avoid this, lock stdout with [`io::stdout().lock()`][lock]:
/// ```
/// use std::io::{stdout, Write};
///
/// let mut lock = stdout().lock();
/// writeln!(lock, "hello world").unwrap();
/// ```
///
/// Use `println!` only for the primary output of your program. Use
/// [`eprintln!`] instead to print error and progress messages.
///
/// See the formatting documentation in [`std::fmt`](crate::fmt)
/// for details of the macro argument syntax.
///
/// [`std::fmt`]: crate::fmt
/// [`eprintln!`]: crate::eprintln
/// [lock]: crate::io::Stdout
///
/// # Panics
///
/// Panics if writing to [`io::stdout`] fails.
///
/// Writing to non-blocking stdout can cause an error, which will lead
/// this macro to panic.
///
/// [`io::stdout`]: crate::io::stdout
///
/// # Examples
///
/// ```
/// println!(); // prints just a newline
/// println!("hello there!");
/// println!("format {} arguments", "some");
/// let local_variable = "some";
/// println!("format {local_variable} arguments");
/// ```
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "println_macro")]
#[allow_internal_unstable(print_internals, format_args_nl)]
macro_rules! println {
() => {
$crate::print!("\n")
};
($($arg:tt)*) => {{
$crate::io::_print($crate::format_args_nl!($($arg)*));
}};
}
/// Prints to the standard error.
///
/// Equivalent to the [`print!`] macro, except that output goes to
/// [`io::stderr`] instead of [`io::stdout`]. See [`print!`] for
/// example usage.
///
/// Use `eprint!` only for error and progress messages. Use `print!`
/// instead for the primary output of your program.
///
/// [`io::stderr`]: crate::io::stderr
/// [`io::stdout`]: crate::io::stdout
///
/// See the formatting documentation in [`std::fmt`](crate::fmt)
/// for details of the macro argument syntax.
///
/// # Panics
///
/// Panics if writing to `io::stderr` fails.
///
/// Writing to non-blocking stderr can cause an error, which will lead
/// this macro to panic.
///
/// # Examples
///
/// ```
/// eprint!("Error: Could not complete task");
/// ```
#[macro_export]
#[stable(feature = "eprint", since = "1.19.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "eprint_macro")]
#[allow_internal_unstable(print_internals)]
macro_rules! eprint {
($($arg:tt)*) => {{
$crate::io::_eprint($crate::format_args!($($arg)*));
}};
}
/// Prints to the standard error, with a newline.
///
/// Equivalent to the [`println!`] macro, except that output goes to
/// [`io::stderr`] instead of [`io::stdout`]. See [`println!`] for
/// example usage.
///
/// Use `eprintln!` only for error and progress messages. Use `println!`
/// instead for the primary output of your program.
///
/// See the formatting documentation in [`std::fmt`](crate::fmt)
/// for details of the macro argument syntax.
///
/// [`io::stderr`]: crate::io::stderr
/// [`io::stdout`]: crate::io::stdout
/// [`println!`]: crate::println
///
/// # Panics
///
/// Panics if writing to `io::stderr` fails.
///
/// Writing to non-blocking stderr can cause an error, which will lead
/// this macro to panic.
///
/// # Examples
///
/// ```
/// eprintln!("Error: Could not complete task");
/// ```
#[macro_export]
#[stable(feature = "eprint", since = "1.19.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "eprintln_macro")]
#[allow_internal_unstable(print_internals, format_args_nl)]
macro_rules! eprintln {
() => {
$crate::eprint!("\n")
};
($($arg:tt)*) => {{
$crate::io::_eprint($crate::format_args_nl!($($arg)*));
}};
}
/// Prints and returns the value of a given expression for quick and dirty
/// debugging.
///
/// An example:
///
/// ```rust
/// let a = 2;
/// let b = dbg!(a * 2) + 1;
/// // ^-- prints: [src/main.rs:2:9] a * 2 = 4
/// assert_eq!(b, 5);
/// ```
///
/// The macro works by using the `Debug` implementation of the type of
/// the given expression to print the value to [stderr] along with the
/// source location of the macro invocation as well as the source code
/// of the expression.
///
/// Invoking the macro on an expression moves and takes ownership of it
/// before returning the evaluated expression unchanged. If the type
/// of the expression does not implement `Copy` and you don't want
/// to give up ownership, you can instead borrow with `dbg!(&expr)`
/// for some expression `expr`.
///
/// The `dbg!` macro works exactly the same in release builds.
/// This is useful when debugging issues that only occur in release
/// builds or when debugging in release mode is significantly faster.
///
/// Note that the macro is intended as a debugging tool and therefore you
/// should avoid having uses of it in version control for long periods
/// (other than in tests and similar).
/// Debug output from production code is better done with other facilities
/// such as the [`debug!`] macro from the [`log`] crate.
///
/// # Stability
///
/// The exact output printed by this macro should not be relied upon
/// and is subject to future changes.
///
/// # Panics
///
/// Panics if writing to `io::stderr` fails.
///
/// # Further examples
///
/// With a method call:
///
/// ```rust
/// fn foo(n: usize) {
/// if let Some(_) = dbg!(n.checked_sub(4)) {
/// // ...
/// }
/// }
///
/// foo(3)
/// ```
///
/// This prints to [stderr]:
///
/// ```text,ignore
/// [src/main.rs:2:22] n.checked_sub(4) = None
/// ```
///
/// Naive factorial implementation:
///
/// ```rust
/// fn factorial(n: u32) -> u32 {
/// if dbg!(n <= 1) {
/// dbg!(1)
/// } else {
/// dbg!(n * factorial(n - 1))
/// }
/// }
///
/// dbg!(factorial(4));
/// ```
///
/// This prints to [stderr]:
///
/// ```text,ignore
/// [src/main.rs:2:8] n <= 1 = false
/// [src/main.rs:2:8] n <= 1 = false
/// [src/main.rs:2:8] n <= 1 = false
/// [src/main.rs:2:8] n <= 1 = true
/// [src/main.rs:3:9] 1 = 1
/// [src/main.rs:7:9] n * factorial(n - 1) = 2
/// [src/main.rs:7:9] n * factorial(n - 1) = 6
/// [src/main.rs:7:9] n * factorial(n - 1) = 24
/// [src/main.rs:9:1] factorial(4) = 24
/// ```
///
/// The `dbg!(..)` macro moves the input:
///
/// ```compile_fail
/// /// A wrapper around `usize` which importantly is not Copyable.
/// #[derive(Debug)]
/// struct NoCopy(usize);
///
/// let a = NoCopy(42);
/// let _ = dbg!(a); // <-- `a` is moved here.
/// let _ = dbg!(a); // <-- `a` is moved again; error!
/// ```
///
/// You can also use `dbg!()` without a value to just print the
/// file and line whenever it's reached.
///
/// Finally, if you want to `dbg!(..)` multiple values, it will treat them as
/// a tuple (and return it, too):
///
/// ```
/// assert_eq!(dbg!(1usize, 2u32), (1, 2));
/// ```
///
/// However, a single argument with a trailing comma will still not be treated
/// as a tuple, following the convention of ignoring trailing commas in macro
/// invocations. You can use a 1-tuple directly if you need one:
///
/// ```
/// assert_eq!(1, dbg!(1u32,)); // trailing comma ignored
/// assert_eq!((1,), dbg!((1u32,))); // 1-tuple
/// ```
///
/// [stderr]: https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)
/// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html
/// [`log`]: https://crates.io/crates/log
#[macro_export]
#[allow_internal_unstable(std_internals)]
#[cfg_attr(not(test), rustc_diagnostic_item = "dbg_macro")]
#[stable(feature = "dbg_macro", since = "1.32.0")]
macro_rules! dbg {
() => {
$crate::eprintln!("[{}:{}:{}]", $crate::file!(), $crate::line!(), $crate::column!())
};
($($val:expr),+ $(,)?) => {
$crate::macros::dbg_internal!(() () ($($val),+))
};
}
/// Internal macro that processes a list of expressions and produces a chain of
/// nested `match`es, one for each expression, before finally calling `eprint!`
/// with the collected information and returning all the evaluated expressions
/// in a tuple.
///
/// E.g. `dbg_internal!(() () (1, 2))` expands into
/// ```rust, ignore
/// match 1 {
/// tmp_1 => match 2 {
/// tmp_2 => {
/// eprint!("...", &tmp_1, &tmp_2, /* some other arguments */);
/// (tmp_1, tmp_2)
/// }
/// }
/// }
/// ```
///
/// This is necessary so that `dbg!` outputs don't get torn, see #136703.
#[doc(hidden)]
#[rustc_macro_transparency = "semiopaque"]
pub macro dbg_internal {
(($($piece:literal),+) ($($processed:expr => $bound:expr),+) ()) => {{
$crate::eprint!(
$crate::concat!($($piece),+),
$(
$crate::stringify!($processed),
// The `&T: Debug` check happens here (not in the format literal desugaring)
// to avoid format literal related messages and suggestions.
&&$bound as &dyn $crate::fmt::Debug
),+,
// The location returned here is that of the macro invocation, so
// it will be the same for all expressions. Thus, label these
// arguments so that they can be reused in every piece of the
// formatting template.
file=$crate::file!(),
line=$crate::line!(),
column=$crate::column!()
);
// Comma separate the variables only when necessary so that this will
// not yield a tuple for a single expression, but rather just parenthesize
// the expression.
($($bound),+)
}},
(($($piece:literal),*) ($($processed:expr => $bound:expr),*) ($val:expr $(,$rest:expr)*)) => {
// Use of `match` here is intentional because it affects the lifetimes
// of temporaries - https://stackoverflow.com/a/48732525/1063961
match $val {
tmp => $crate::macros::dbg_internal!(
($($piece,)* "[{file}:{line}:{column}] {} = {:#?}\n")
($($processed => $bound,)* $val => tmp)
($($rest),*)
),
}
},
}

3
crates/std/src/pat.rs Normal file
View File

@@ -0,0 +1,3 @@
//! Helper module for exporting the `pattern_type` macro
pub use core::pattern_type;

View File

@@ -92,8 +92,8 @@ use crate::iter::FusedIterator;
use crate::ops::{self, Deref}; use crate::ops::{self, Deref};
use crate::rc::Rc; use crate::rc::Rc;
use crate::str::FromStr; use crate::str::FromStr;
use alloc_crate::sync::Arc; use crate::sync::Arc;
use crate::sys::path::{HAS_PREFIXES, MAIN_SEP_STR, is_sep_byte, is_verbatim_sep, parse_prefix}; use crate::sys::path::{HAS_PREFIXES, is_sep_byte, is_verbatim_sep, parse_prefix};
use crate::{cmp, fmt, fs, io, sys}; use crate::{cmp, fmt, fs, io, sys};
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -266,22 +266,33 @@ impl<'a> Prefix<'a> {
/// ``` /// ```
#[must_use] #[must_use]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn is_separator(c: char) -> bool { #[rustc_const_unstable(feature = "const_path_separators", issue = "153106")]
pub const fn is_separator(c: char) -> bool {
c.is_ascii() && is_sep_byte(c as u8) c.is_ascii() && is_sep_byte(c as u8)
} }
/// The primary separator of path components for the current platform. /// All path separators recognized on the current platform, represented as [`char`]s; for example,
/// /// this is `&['/'][..]` on Unix and `&['\\', '/'][..]` on Windows. The [primary
/// For example, `/` on Unix and `\` on Windows. /// separator](MAIN_SEPARATOR) is always element 0 of the slice.
#[unstable(feature = "const_path_separators", issue = "153106")]
pub const SEPARATORS: &[char] = crate::sys::path::SEPARATORS;
/// All path separators recognized on the current platform, represented as [`&str`]s; for example,
/// this is `&["/"][..]` on Unix and `&["\\", "/"][..]` on Windows. The [primary
/// separator](MAIN_SEPARATOR_STR) is always element 0 of the slice.
#[unstable(feature = "const_path_separators", issue = "153106")]
pub const SEPARATORS_STR: &[&str] = crate::sys::path::SEPARATORS_STR;
/// The primary separator of path components for the current platform, represented as a [`char`];
/// for example, this is `'/'` on Unix and `'\\'` on Windows.
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "path_main_separator")] #[cfg_attr(not(test), rustc_diagnostic_item = "path_main_separator")]
pub const MAIN_SEPARATOR: char = crate::sys::path::MAIN_SEP; pub const MAIN_SEPARATOR: char = SEPARATORS[0];
/// The primary separator of path components for the current platform. /// The primary separator of path components for the current platform, represented as a [`&str`];
/// /// for example, this is `"/"` on Unix and `"\\"` on Windows.
/// For example, `/` on Unix and `\` on Windows.
#[stable(feature = "main_separator_str", since = "1.68.0")] #[stable(feature = "main_separator_str", since = "1.68.0")]
pub const MAIN_SEPARATOR_STR: &str = crate::sys::path::MAIN_SEP_STR; pub const MAIN_SEPARATOR_STR: &str = SEPARATORS_STR[0];
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Misc helpers // Misc helpers
@@ -562,7 +573,7 @@ impl<'a> Component<'a> {
pub fn as_os_str(self) -> &'a OsStr { pub fn as_os_str(self) -> &'a OsStr {
match self { match self {
Component::Prefix(p) => p.as_os_str(), Component::Prefix(p) => p.as_os_str(),
Component::RootDir => OsStr::new(MAIN_SEP_STR), Component::RootDir => OsStr::new(MAIN_SEPARATOR_STR),
Component::CurDir => OsStr::new("."), Component::CurDir => OsStr::new("."),
Component::ParentDir => OsStr::new(".."), Component::ParentDir => OsStr::new(".."),
Component::Normal(path) => path, Component::Normal(path) => path,
@@ -1379,7 +1390,7 @@ impl PathBuf {
for c in buf { for c in buf {
if need_sep && c != Component::RootDir { if need_sep && c != Component::RootDir {
res.push(MAIN_SEP_STR); res.push(MAIN_SEPARATOR_STR);
} }
res.push(c.as_os_str()); res.push(c.as_os_str());
@@ -1402,7 +1413,7 @@ impl PathBuf {
// `path` is a pure relative path // `path` is a pure relative path
} else if need_sep { } else if need_sep {
self.inner.push(MAIN_SEP_STR); self.inner.push(MAIN_SEPARATOR_STR);
} }
self.inner.push(path); self.inner.push(path);
@@ -3240,73 +3251,101 @@ impl Path {
self self
} }
// /// Queries the file system to get information about a file, directory, etc. /// Queries the file system to get information about a file, directory, etc.
// /// ///
// /// This function will traverse symbolic links to query information about the /// This function will traverse symbolic links to query information about the
// /// destination file. /// destination file.
// /// ///
// /// This is an alias to [`fs::metadata`]. /// This is an alias to [`fs::metadata`].
// /// ///
// /// # Examples /// # Examples
// /// ///
// /// ```no_run /// ```no_run
// /// use std::path::Path; /// use std::path::Path;
// /// ///
// /// let path = Path::new("/Minas/tirith"); /// let path = Path::new("/Minas/tirith");
// /// let metadata = path.metadata().expect("metadata call failed"); /// let metadata = path.metadata().expect("metadata call failed");
// /// println!("{:?}", metadata.file_type()); /// println!("{:?}", metadata.file_type());
// /// ``` /// ```
// #[stable(feature = "path_ext", since = "1.5.0")] #[stable(feature = "path_ext", since = "1.5.0")]
// #[inline] #[inline]
// pub fn metadata(&self) -> io::Result<fs::Metadata> { pub fn metadata(&self) -> io::Result<fs::Metadata> {
// fs::metadata(self) fs::metadata(self)
// } }
// /// Queries the metadata about a file without following symlinks. /// Queries the metadata about a file without following symlinks.
// /// ///
// /// This is an alias to [`fs::symlink_metadata`]. /// This is an alias to [`fs::symlink_metadata`].
// /// ///
// /// # Examples /// # Examples
// /// ///
// /// ```no_run /// ```no_run
// /// use std::path::Path; /// use std::path::Path;
// /// ///
// /// let path = Path::new("/Minas/tirith"); /// let path = Path::new("/Minas/tirith");
// /// let metadata = path.symlink_metadata().expect("symlink_metadata call failed"); /// let metadata = path.symlink_metadata().expect("symlink_metadata call failed");
// /// println!("{:?}", metadata.file_type()); /// println!("{:?}", metadata.file_type());
// /// ``` /// ```
// #[stable(feature = "path_ext", since = "1.5.0")] #[stable(feature = "path_ext", since = "1.5.0")]
// #[inline] #[inline]
// pub fn symlink_metadata(&self) -> io::Result<fs::Metadata> { pub fn symlink_metadata(&self) -> io::Result<fs::Metadata> {
// fs::symlink_metadata(self) fs::symlink_metadata(self)
// } }
// /// Returns the canonical, absolute form of the path with all intermediate /// Returns the canonical, absolute form of the path with all intermediate
// /// components normalized and symbolic links resolved. /// components normalized and symbolic links resolved.
// /// ///
// /// This is an alias to [`fs::canonicalize`]. /// This is an alias to [`fs::canonicalize`].
// /// ///
// /// # Errors /// # Errors
// /// ///
// /// This method will return an error in the following situations, but is not /// This method will return an error in the following situations, but is not
// /// limited to just these cases: /// limited to just these cases:
// /// ///
// /// * `path` does not exist. /// * `path` does not exist.
// /// * A non-final component in path is not a directory. /// * A non-final component in path is not a directory.
// /// ///
// /// # Examples /// # Examples
// /// ///
// /// ```no_run /// ```no_run
// /// use std::path::{Path, PathBuf}; /// use std::path::{Path, PathBuf};
// /// ///
// /// let path = Path::new("/foo/test/../test/bar.rs"); /// let path = Path::new("/foo/test/../test/bar.rs");
// /// assert_eq!(path.canonicalize().unwrap(), PathBuf::from("/foo/test/bar.rs")); /// assert_eq!(path.canonicalize().unwrap(), PathBuf::from("/foo/test/bar.rs"));
// /// ``` /// ```
// #[stable(feature = "path_ext", since = "1.5.0")] #[stable(feature = "path_ext", since = "1.5.0")]
// #[inline] #[inline]
// pub fn canonicalize(&self) -> io::Result<PathBuf> { pub fn canonicalize(&self) -> io::Result<PathBuf> {
// fs::canonicalize(self) fs::canonicalize(self)
// } }
/// Makes the path absolute without accessing the filesystem.
///
/// This is an alias to [`path::absolute`](absolute).
///
/// # Errors
///
/// This function may return an error in the following situations:
///
/// * If the path is syntactically invalid; in particular, if it is empty.
/// * If getting the [current directory][crate::env::current_dir] fails.
///
/// # Examples
///
/// ```no_run
/// #![feature(path_absolute_method)]
/// use std::path::Path;
///
/// let path = Path::new("foo/./bar");
/// let absolute = path.absolute()?;
/// assert!(absolute.is_absolute());
/// # Ok::<(), std::io::Error>(())
/// ```
#[unstable(feature = "path_absolute_method", issue = "153328")]
#[inline]
pub fn absolute(&self) -> io::Result<PathBuf> {
absolute(self)
}
/// Normalize a path, including `..` without traversing the filesystem. /// Normalize a path, including `..` without traversing the filesystem.
/// ///
@@ -3369,206 +3408,206 @@ impl Path {
Ok(lexical) Ok(lexical)
} }
// /// Reads a symbolic link, returning the file that the link points to. /// Reads a symbolic link, returning the file that the link points to.
// /// ///
// /// This is an alias to [`fs::read_link`]. /// This is an alias to [`fs::read_link`].
// /// ///
// /// # Examples /// # Examples
// /// ///
// /// ```no_run /// ```no_run
// /// use std::path::Path; /// use std::path::Path;
// /// ///
// /// let path = Path::new("/laputa/sky_castle.rs"); /// let path = Path::new("/laputa/sky_castle.rs");
// /// let path_link = path.read_link().expect("read_link call failed"); /// let path_link = path.read_link().expect("read_link call failed");
// /// ``` /// ```
// #[stable(feature = "path_ext", since = "1.5.0")] #[stable(feature = "path_ext", since = "1.5.0")]
// #[inline] #[inline]
// pub fn read_link(&self) -> io::Result<PathBuf> { pub fn read_link(&self) -> io::Result<PathBuf> {
// fs::read_link(self) fs::read_link(self)
// } }
// /// Returns an iterator over the entries within a directory. /// Returns an iterator over the entries within a directory.
// /// ///
// /// The iterator will yield instances of <code>[io::Result]<[fs::DirEntry]></code>. New /// The iterator will yield instances of <code>[io::Result]<[fs::DirEntry]></code>. New
// /// errors may be encountered after an iterator is initially constructed. /// errors may be encountered after an iterator is initially constructed.
// /// ///
// /// This is an alias to [`fs::read_dir`]. /// This is an alias to [`fs::read_dir`].
// /// ///
// /// # Examples /// # Examples
// /// ///
// /// ```no_run /// ```no_run
// /// use std::path::Path; /// use std::path::Path;
// /// ///
// /// let path = Path::new("/laputa"); /// let path = Path::new("/laputa");
// /// for entry in path.read_dir().expect("read_dir call failed") { /// for entry in path.read_dir().expect("read_dir call failed") {
// /// if let Ok(entry) = entry { /// if let Ok(entry) = entry {
// /// println!("{:?}", entry.path()); /// println!("{:?}", entry.path());
// /// } /// }
// /// } /// }
// /// ``` /// ```
// #[stable(feature = "path_ext", since = "1.5.0")] #[stable(feature = "path_ext", since = "1.5.0")]
// #[inline] #[inline]
// pub fn read_dir(&self) -> io::Result<fs::ReadDir> { pub fn read_dir(&self) -> io::Result<fs::ReadDir> {
// fs::read_dir(self) fs::read_dir(self)
// } }
// /// Returns `true` if the path points at an existing entity. /// Returns `true` if the path points at an existing entity.
// /// ///
// /// Warning: this method may be error-prone, consider using [`try_exists()`] instead! /// Warning: this method may be error-prone, consider using [`try_exists()`] instead!
// /// It also has a risk of introducing time-of-check to time-of-use ([TOCTOU]) bugs. /// It also has a risk of introducing time-of-check to time-of-use ([TOCTOU]) bugs.
// /// ///
// /// This function will traverse symbolic links to query information about the /// This function will traverse symbolic links to query information about the
// /// destination file. /// destination file.
// /// ///
// /// If you cannot access the metadata of the file, e.g. because of a /// If you cannot access the metadata of the file, e.g. because of a
// /// permission error or broken symbolic links, this will return `false`. /// permission error or broken symbolic links, this will return `false`.
// /// ///
// /// # Examples /// # Examples
// /// ///
// /// ```no_run /// ```no_run
// /// use std::path::Path; /// use std::path::Path;
// /// assert!(!Path::new("does_not_exist.txt").exists()); /// assert!(!Path::new("does_not_exist.txt").exists());
// /// ``` /// ```
// /// ///
// /// # See Also /// # See Also
// /// ///
// /// This is a convenience function that coerces errors to false. If you want to /// This is a convenience function that coerces errors to false. If you want to
// /// check errors, call [`Path::try_exists`]. /// check errors, call [`Path::try_exists`].
// /// ///
// /// [`try_exists()`]: Self::try_exists /// [`try_exists()`]: Self::try_exists
// /// [TOCTOU]: fs#time-of-check-to-time-of-use-toctou /// [TOCTOU]: fs#time-of-check-to-time-of-use-toctou
// #[stable(feature = "path_ext", since = "1.5.0")] #[stable(feature = "path_ext", since = "1.5.0")]
// #[must_use] #[must_use]
// #[inline] #[inline]
// pub fn exists(&self) -> bool { pub fn exists(&self) -> bool {
// fs::metadata(self).is_ok() fs::metadata(self).is_ok()
// } }
// /// Returns `Ok(true)` if the path points at an existing entity. /// Returns `Ok(true)` if the path points at an existing entity.
// /// ///
// /// This function will traverse symbolic links to query information about the /// This function will traverse symbolic links to query information about the
// /// destination file. In case of broken symbolic links this will return `Ok(false)`. /// destination file. In case of broken symbolic links this will return `Ok(false)`.
// /// ///
// /// [`Path::exists()`] only checks whether or not a path was both found and readable. By /// [`Path::exists()`] only checks whether or not a path was both found and readable. By
// /// contrast, `try_exists` will return `Ok(true)` or `Ok(false)`, respectively, if the path /// contrast, `try_exists` will return `Ok(true)` or `Ok(false)`, respectively, if the path
// /// was _verified_ to exist or not exist. If its existence can neither be confirmed nor /// was _verified_ to exist or not exist. If its existence can neither be confirmed nor
// /// denied, it will propagate an `Err(_)` instead. This can be the case if e.g. listing /// denied, it will propagate an `Err(_)` instead. This can be the case if e.g. listing
// /// permission is denied on one of the parent directories. /// permission is denied on one of the parent directories.
// /// ///
// /// Note that while this avoids some pitfalls of the `exists()` method, it still can not /// Note that while this avoids some pitfalls of the `exists()` method, it still can not
// /// prevent time-of-check to time-of-use ([TOCTOU]) bugs. You should only use it in scenarios /// prevent time-of-check to time-of-use ([TOCTOU]) bugs. You should only use it in scenarios
// /// where those bugs are not an issue. /// where those bugs are not an issue.
// /// ///
// /// This is an alias for [`std::fs::exists`](crate::fs::exists). /// This is an alias for [`std::fs::exists`](crate::fs::exists).
// /// ///
// /// # Examples /// # Examples
// /// ///
// /// ```no_run /// ```no_run
// /// use std::path::Path; /// use std::path::Path;
// /// assert!(!Path::new("does_not_exist.txt").try_exists().expect("Can't check existence of file does_not_exist.txt")); /// assert!(!Path::new("does_not_exist.txt").try_exists().expect("Can't check existence of file does_not_exist.txt"));
// /// assert!(Path::new("/root/secret_file.txt").try_exists().is_err()); /// assert!(Path::new("/root/secret_file.txt").try_exists().is_err());
// /// ``` /// ```
// /// ///
// /// [TOCTOU]: fs#time-of-check-to-time-of-use-toctou /// [TOCTOU]: fs#time-of-check-to-time-of-use-toctou
// /// [`exists()`]: Self::exists /// [`exists()`]: Self::exists
// #[stable(feature = "path_try_exists", since = "1.63.0")] #[stable(feature = "path_try_exists", since = "1.63.0")]
// #[inline] #[inline]
// pub fn try_exists(&self) -> io::Result<bool> { pub fn try_exists(&self) -> io::Result<bool> {
// fs::exists(self) fs::exists(self)
// } }
// /// Returns `true` if the path exists on disk and is pointing at a regular file. /// Returns `true` if the path exists on disk and is pointing at a regular file.
// /// ///
// /// This function will traverse symbolic links to query information about the /// This function will traverse symbolic links to query information about the
// /// destination file. /// destination file.
// /// ///
// /// If you cannot access the metadata of the file, e.g. because of a /// If you cannot access the metadata of the file, e.g. because of a
// /// permission error or broken symbolic links, this will return `false`. /// permission error or broken symbolic links, this will return `false`.
// /// ///
// /// # Examples /// # Examples
// /// ///
// /// ```no_run /// ```no_run
// /// use std::path::Path; /// use std::path::Path;
// /// assert_eq!(Path::new("./is_a_directory/").is_file(), false); /// assert_eq!(Path::new("./is_a_directory/").is_file(), false);
// /// assert_eq!(Path::new("a_file.txt").is_file(), true); /// assert_eq!(Path::new("a_file.txt").is_file(), true);
// /// ``` /// ```
// /// ///
// /// # See Also /// # See Also
// /// ///
// /// This is a convenience function that coerces errors to false. If you want to /// This is a convenience function that coerces errors to false. If you want to
// /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call
// /// [`fs::Metadata::is_file`] if it was [`Ok`]. /// [`fs::Metadata::is_file`] if it was [`Ok`].
// /// ///
// /// When the goal is simply to read from (or write to) the source, the most /// When the goal is simply to read from (or write to) the source, the most
// /// reliable way to test the source can be read (or written to) is to open /// reliable way to test the source can be read (or written to) is to open
// /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on
// /// a Unix-like system for example. See [`fs::File::open`] or /// a Unix-like system for example. See [`fs::File::open`] or
// /// [`fs::OpenOptions::open`] for more information. /// [`fs::OpenOptions::open`] for more information.
// #[stable(feature = "path_ext", since = "1.5.0")] #[stable(feature = "path_ext", since = "1.5.0")]
// #[must_use] #[must_use]
// pub fn is_file(&self) -> bool { pub fn is_file(&self) -> bool {
// fs::metadata(self).map(|m| m.is_file()).unwrap_or(false) fs::metadata(self).map(|m| m.is_file()).unwrap_or(false)
// } }
// /// Returns `true` if the path exists on disk and is pointing at a directory. /// Returns `true` if the path exists on disk and is pointing at a directory.
// /// ///
// /// This function will traverse symbolic links to query information about the /// This function will traverse symbolic links to query information about the
// /// destination file. /// destination file.
// /// ///
// /// If you cannot access the metadata of the file, e.g. because of a /// If you cannot access the metadata of the file, e.g. because of a
// /// permission error or broken symbolic links, this will return `false`. /// permission error or broken symbolic links, this will return `false`.
// /// ///
// /// # Examples /// # Examples
// /// ///
// /// ```no_run /// ```no_run
// /// use std::path::Path; /// use std::path::Path;
// /// assert_eq!(Path::new("./is_a_directory/").is_dir(), true); /// assert_eq!(Path::new("./is_a_directory/").is_dir(), true);
// /// assert_eq!(Path::new("a_file.txt").is_dir(), false); /// assert_eq!(Path::new("a_file.txt").is_dir(), false);
// /// ``` /// ```
// /// ///
// /// # See Also /// # See Also
// /// ///
// /// This is a convenience function that coerces errors to false. If you want to /// This is a convenience function that coerces errors to false. If you want to
// /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call
// /// [`fs::Metadata::is_dir`] if it was [`Ok`]. /// [`fs::Metadata::is_dir`] if it was [`Ok`].
// #[stable(feature = "path_ext", since = "1.5.0")] #[stable(feature = "path_ext", since = "1.5.0")]
// #[must_use] #[must_use]
// pub fn is_dir(&self) -> bool { pub fn is_dir(&self) -> bool {
// fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false) fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false)
// } }
// /// Returns `true` if the path exists on disk and is pointing at a symbolic link. /// Returns `true` if the path exists on disk and is pointing at a symbolic link.
// /// ///
// /// This function will not traverse symbolic links. /// This function will not traverse symbolic links.
// /// In case of a broken symbolic link this will also return true. /// In case of a broken symbolic link this will also return true.
// /// ///
// /// If you cannot access the directory containing the file, e.g., because of a /// If you cannot access the directory containing the file, e.g., because of a
// /// permission error, this will return false. /// permission error, this will return false.
// /// ///
// /// # Examples /// # Examples
// /// ///
// /// ```rust,no_run /// ```rust,no_run
// /// # #[cfg(unix)] { /// # #[cfg(unix)] {
// /// use std::path::Path; /// use std::path::Path;
// /// use std::os::unix::fs::symlink; /// use std::os::unix::fs::symlink;
// /// ///
// /// let link_path = Path::new("link"); /// let link_path = Path::new("link");
// /// symlink("/origin_does_not_exist/", link_path).unwrap(); /// symlink("/origin_does_not_exist/", link_path).unwrap();
// /// assert_eq!(link_path.is_symlink(), true); /// assert_eq!(link_path.is_symlink(), true);
// /// assert_eq!(link_path.exists(), false); /// assert_eq!(link_path.exists(), false);
// /// # } /// # }
// /// ``` /// ```
// /// ///
// /// # See Also /// # See Also
// /// ///
// /// This is a convenience function that coerces errors to false. If you want to /// This is a convenience function that coerces errors to false. If you want to
// /// check errors, call [`fs::symlink_metadata`] and handle its [`Result`]. Then call /// check errors, call [`fs::symlink_metadata`] and handle its [`Result`]. Then call
// /// [`fs::Metadata::is_symlink`] if it was [`Ok`]. /// [`fs::Metadata::is_symlink`] if it was [`Ok`].
// #[must_use] #[must_use]
// #[stable(feature = "is_symlink", since = "1.58.0")] #[stable(feature = "is_symlink", since = "1.58.0")]
// pub fn is_symlink(&self) -> bool { pub fn is_symlink(&self) -> bool {
// fs::symlink_metadata(self).map(|m| m.is_symlink()).unwrap_or(false) fs::symlink_metadata(self).map(|m| m.is_symlink()).unwrap_or(false)
// } }
/// Converts a [`Box<Path>`](Box) into a [`PathBuf`] without copying or /// Converts a [`Box<Path>`](Box) into a [`PathBuf`] without copying or
/// allocating. /// allocating.

View File

@@ -17,6 +17,10 @@ pub mod rust_2024 {
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::vec::Vec; pub use crate::vec::Vec;
#[stable(feature = "rust1", since = "1.0.0")]
#[doc(no_inline)]
pub use crate::option::Option::{self, None, Some};
// Re-exported built-in macros and traits // Re-exported built-in macros and traits
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
#[doc(no_inline)] #[doc(no_inline)]
@@ -150,12 +154,6 @@ pub mod rust_2024 {
} }
} }
// #[panic_handler]
// fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
// // TODO print
// loop {}
// }
/// # Safety /// # Safety
/// `argc` and `argv` are passed by the kernel /// `argc` and `argv` are passed by the kernel
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
@@ -166,16 +164,4 @@ pub mod rust_2024 {
unsafe { main(argc, argv) } unsafe { main(argc, argv) }
} }
#[lang = "start"]
pub fn lang_start<T: crate::process::Termination + 'static>(
main: fn() -> T,
argc: isize,
argv: *const *const u8,
_sigpipe: u8,
) -> isize {
println!("{}", argc);
println!("{:?}", argv);
main().report().to_i32() as isize
}
} }

View File

@@ -156,6 +156,7 @@
target_env = "sgx", target_env = "sgx",
target_os = "xous", target_os = "xous",
target_os = "trusty", target_os = "trusty",
target_os = "hermit",
)) ))
))] ))]
mod tests; mod tests;
@@ -1380,6 +1381,7 @@ impl Output {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # #![allow(unused_features)]
/// #![feature(exit_status_error)] /// #![feature(exit_status_error)]
/// # #[cfg(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos")))))] { /// # #[cfg(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos")))))] {
/// use std::process::Command; /// use std::process::Command;
@@ -1679,32 +1681,32 @@ impl From<ChildStderr> for Stdio {
} }
} }
// #[stable(feature = "stdio_from", since = "1.20.0")] #[stable(feature = "stdio_from", since = "1.20.0")]
// impl From<fs::File> for Stdio { impl From<fs::File> for Stdio {
// /// Converts a [`File`](fs::File) into a [`Stdio`]. /// Converts a [`File`](fs::File) into a [`Stdio`].
// /// ///
// /// # Examples /// # Examples
// /// ///
// /// `File` will be converted to `Stdio` using `Stdio::from` under the hood. /// `File` will be converted to `Stdio` using `Stdio::from` under the hood.
// /// ///
// /// ```rust,no_run /// ```rust,no_run
// /// use std::fs::File; /// use std::fs::File;
// /// use std::process::Command; /// use std::process::Command;
// /// ///
// /// // With the `foo.txt` file containing "Hello, world!" /// // With the `foo.txt` file containing "Hello, world!"
// /// let file = File::open("foo.txt")?; /// let file = File::open("foo.txt")?;
// /// ///
// /// let reverse = Command::new("rev") /// let reverse = Command::new("rev")
// /// .stdin(file) // Implicit File conversion into a Stdio /// .stdin(file) // Implicit File conversion into a Stdio
// /// .output()?; /// .output()?;
// /// ///
// /// assert_eq!(reverse.stdout, b"!dlrow ,olleH"); /// assert_eq!(reverse.stdout, b"!dlrow ,olleH");
// /// # std::io::Result::Ok(()) /// # std::io::Result::Ok(())
// /// ``` /// ```
// fn from(file: fs::File) -> Stdio { fn from(file: fs::File) -> Stdio {
// Stdio::from_inner(file.into_inner().into()) Stdio::from_inner(file.into_inner().into())
// } }
// } }
#[stable(feature = "stdio_from_stdio", since = "1.74.0")] #[stable(feature = "stdio_from_stdio", since = "1.74.0")]
impl From<io::Stdout> for Stdio { impl From<io::Stdout> for Stdio {
@@ -1959,6 +1961,7 @@ impl crate::sealed::Sealed for ExitStatusError {}
pub struct ExitStatusError(imp::ExitStatusError); pub struct ExitStatusError(imp::ExitStatusError);
#[unstable(feature = "exit_status_error", issue = "84908")] #[unstable(feature = "exit_status_error", issue = "84908")]
#[doc(test(attr(allow(unused_features))))]
impl ExitStatusError { impl ExitStatusError {
/// Reports the exit code, if applicable, from an `ExitStatusError`. /// Reports the exit code, if applicable, from an `ExitStatusError`.
/// ///
@@ -2545,7 +2548,7 @@ pub fn abort() -> ! {
#[must_use] #[must_use]
#[stable(feature = "getpid", since = "1.26.0")] #[stable(feature = "getpid", since = "1.26.0")]
pub fn id() -> u32 { pub fn id() -> u32 {
todo!() imp::getpid()
} }
/// A trait for implementing arbitrary return types in the `main` function. /// A trait for implementing arbitrary return types in the `main` function.

96
crates/std/src/random.rs Normal file
View File

@@ -0,0 +1,96 @@
//! Random value generation.
#[unstable(feature = "random", issue = "130703")]
pub use core::random::*;
use crate::sys::random as sys;
/// The default random source.
///
/// This asks the system for random data suitable for cryptographic purposes
/// such as key generation. If security is a concern, consult the platform
/// documentation below for the specific guarantees your target provides.
///
/// The high quality of randomness provided by this source means it can be quite
/// slow on some targets. If you need a large quantity of random numbers and
/// security is not a concern, consider using an alternative random number
/// generator (potentially seeded from this one).
///
/// # Underlying sources
///
/// Platform | Source
/// -----------------------|---------------------------------------------------------------
/// Linux | [`getrandom`] or [`/dev/urandom`] after polling `/dev/random`
/// Windows | [`ProcessPrng`](https://learn.microsoft.com/en-us/windows/win32/seccng/processprng)
/// Apple | `CCRandomGenerateBytes`
/// DragonFly | [`arc4random_buf`](https://man.dragonflybsd.org/?command=arc4random)
/// ESP-IDF | [`esp_fill_random`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t)
/// FreeBSD | [`arc4random_buf`](https://man.freebsd.org/cgi/man.cgi?query=arc4random)
/// Fuchsia | [`cprng_draw`](https://fuchsia.dev/reference/syscalls/cprng_draw)
/// Haiku | `arc4random_buf`
/// Illumos | [`arc4random_buf`](https://www.illumos.org/man/3C/arc4random)
/// NetBSD | [`arc4random_buf`](https://man.netbsd.org/arc4random.3)
/// OpenBSD | [`arc4random_buf`](https://man.openbsd.org/arc4random.3)
/// Solaris | [`arc4random_buf`](https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html)
/// Vita | `arc4random_buf`
/// Hermit | `read_entropy`
/// Horizon, Cygwin | `getrandom`
/// AIX, Hurd, L4Re, QNX | `/dev/urandom`
/// Redox | `/scheme/rand`
/// RTEMS | [`arc4random_buf`](https://docs.rtems.org/branches/main/bsp-howto/getentropy.html)
/// SGX | [`rdrand`](https://en.wikipedia.org/wiki/RDRAND)
/// SOLID | `SOLID_RNG_SampleRandomBytes`
/// TEEOS | `TEE_GenerateRandom`
/// UEFI | [`EFI_RNG_PROTOCOL`](https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol)
/// VxWorks | `randABytes` after waiting for `randSecure` to become ready
/// WASI | [`random_get`](https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-random_getbuf-pointeru8-buf_len-size---result-errno)
/// ZKVM | `sys_rand`
///
/// Note that the sources used might change over time.
///
/// Consult the documentation for the underlying operations on your supported
/// targets to determine whether they provide any particular desired properties,
/// such as support for reseeding on VM fork operations.
///
/// [`getrandom`]: https://www.man7.org/linux/man-pages/man2/getrandom.2.html
/// [`/dev/urandom`]: https://www.man7.org/linux/man-pages/man4/random.4.html
#[derive(Default, Debug, Clone, Copy)]
#[unstable(feature = "random", issue = "130703")]
pub struct DefaultRandomSource;
#[unstable(feature = "random", issue = "130703")]
impl RandomSource for DefaultRandomSource {
fn fill_bytes(&mut self, bytes: &mut [u8]) {
sys::fill_bytes(bytes)
}
}
/// Generates a random value from a distribution, using the default random source.
///
/// This is a convenience function for `dist.sample(&mut DefaultRandomSource)` and will sample
/// according to the same distribution as the underlying [`Distribution`] trait implementation. See
/// [`DefaultRandomSource`] for more information about how randomness is sourced.
///
/// # Examples
///
/// Generating a [version 4/variant 1 UUID] represented as text:
/// ```
/// #![feature(random)]
///
/// use std::random::random;
///
/// let bits: u128 = random(..);
/// let g1 = (bits >> 96) as u32;
/// let g2 = (bits >> 80) as u16;
/// let g3 = (0x4000 | (bits >> 64) & 0x0fff) as u16;
/// let g4 = (0x8000 | (bits >> 48) & 0x3fff) as u16;
/// let g5 = (bits & 0xffffffffffff) as u64;
/// let uuid = format!("{g1:08x}-{g2:04x}-{g3:04x}-{g4:04x}-{g5:012x}");
/// println!("{uuid}");
/// ```
///
/// [version 4/variant 1 UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
#[unstable(feature = "random", issue = "130703")]
pub fn random<T>(dist: impl Distribution<T>) -> T {
dist.sample(&mut DefaultRandomSource)
}

View File

@@ -1,8 +1,211 @@
macro_rules! rtabort { //! Runtime services
($($t:tt)*) => {{ loop {} }}; //!
} //! The `rt` module provides a narrow set of runtime services,
macro_rules! rtprintpanic { //! including the global heap (exported in `heap`) and unwinding and
($($t:tt)*) => {{}}; //! backtrace support. The APIs in this module are highly unstable,
//! and should be considered as private implementation details for the
//! time being.
#![unstable(
feature = "rt",
reason = "this public module should not exist and is highly likely \
to disappear",
issue = "none"
)]
#![doc(hidden)]
#![deny(unsafe_op_in_unsafe_fn)]
#![allow(unused_macros)]
#[rustfmt::skip]
pub use crate::panicking::{begin_panic, panic_count};
pub use core::panicking::{panic_display, panic_fmt};
#[rustfmt::skip]
use crate::any::Any;
use crate::sync::Once;
use crate::thread::{self, main_thread};
use crate::{mem, panic, sys};
// This function is needed by the panic runtime.
#[cfg(not(test))]
#[rustc_std_internal_symbol]
fn __rust_abort() {
crate::process::abort();
} }
pub fn cleanup() {} // Prints to the "panic output", depending on the platform this may be:
// - the standard error output
// - some dedicated platform specific output
// - nothing (so this macro is a no-op)
macro_rules! rtprintpanic {
($($t:tt)*) => {
#[cfg(not(panic = "immediate-abort"))]
if let Some(mut out) = crate::sys::stdio::panic_output() {
let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*));
}
#[cfg(panic = "immediate-abort")]
{
let _ = format_args!($($t)*);
}
}
}
macro_rules! rtabort {
($($t:tt)*) => {
{
rtprintpanic!("fatal runtime error: {}, aborting\n", format_args!($($t)*));
crate::process::abort();
}
}
}
macro_rules! rtassert {
($e:expr) => {
if !$e {
rtabort!(concat!("assertion failed: ", stringify!($e)));
}
};
}
macro_rules! rtunwrap {
($ok:ident, $e:expr) => {
match $e {
$ok(v) => v,
ref err => {
let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
}
}
};
}
fn handle_rt_panic<T>(e: Box<dyn Any + Send>) -> T {
mem::forget(e);
rtabort!("initialization or cleanup bug");
}
// One-time runtime initialization.
// Runs before `main`.
// SAFETY: must be called only once during runtime initialization.
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
//
// # The `sigpipe` parameter
//
// Since 2014, the Rust runtime on Unix has set the `SIGPIPE` handler to
// `SIG_IGN`. Applications have good reasons to want a different behavior
// though, so there is a `-Zon-broken-pipe` compiler flag that
// can be used to select how `SIGPIPE` shall be setup (if changed at all) before
// `fn main()` is called. See <https://github.com/rust-lang/rust/issues/97889>
// for more info.
//
// The `sigpipe` parameter to this function gets its value via the code that
// rustc generates to invoke `fn lang_start()`. The reason we have `sigpipe` for
// all platforms and not only Unix, is because std is not allowed to have `cfg`
// directives as this high level. See the module docs in
// `src/tools/tidy/src/pal.rs` for more info. On all other platforms, `sigpipe`
// has a value, but its value is ignored.
//
// Even though it is an `u8`, it only ever has 4 values. These are documented in
// `compiler/rustc_session/src/config/sigpipe.rs`.
#[cfg_attr(test, allow(dead_code))]
unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
// Remember the main thread ID to give it the correct name.
// SAFETY: this is the only time and place where we call this function.
unsafe { main_thread::set(thread::current_id()) };
#[cfg_attr(target_os = "teeos", allow(unused_unsafe))]
unsafe {
sys::init(argc, argv, sigpipe)
};
}
/// Clean up the thread-local runtime state. This *should* be run after all other
/// code managed by the Rust runtime, but will not cause UB if that condition is
/// not fulfilled. Also note that this function is not guaranteed to be run, but
/// skipping it will cause leaks and therefore is to be avoided.
pub(crate) fn thread_cleanup() {
// This function is run in situations where unwinding leads to an abort
// (think `extern "C"` functions). Abort here instead so that we can
// print a nice message.
panic::catch_unwind(|| {
crate::thread::drop_current();
})
.unwrap_or_else(handle_rt_panic);
}
// One-time runtime cleanup.
// Runs after `main` or at program exit.
// NOTE: this is not guaranteed to run, for example when the program aborts.
pub(crate) fn cleanup() {
static CLEANUP: Once = Once::new();
CLEANUP.call_once(|| unsafe {
// Flush stdout and disable buffering.
crate::io::cleanup();
// SAFETY: Only called once during runtime cleanup.
sys::cleanup();
});
}
// To reduce the generated code of the new `lang_start`, this function is doing
// the real work.
#[cfg(not(test))]
fn lang_start_internal(
main: &(dyn Fn() -> i32 + Sync + crate::panic::RefUnwindSafe),
argc: isize,
argv: *const *const u8,
sigpipe: u8,
) -> isize {
// Guard against the code called by this function from unwinding outside of the Rust-controlled
// code, which is UB. This is a requirement imposed by a combination of how the
// `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking
// mechanism itself.
//
// There are a couple of instances where unwinding can begin. First is inside of the
// `rt::init`, `rt::cleanup` and similar functions controlled by std. In those instances a
// panic is a std implementation bug. A quite likely one too, as there isn't any way to
// prevent std from accidentally introducing a panic to these functions. Another is from
// user code from `main` or, more nefariously, as described in e.g. issue #86030.
//
// We use `catch_unwind` with `handle_rt_panic` instead of `abort_unwind` to make the error in
// case of a panic a bit nicer.
panic::catch_unwind(move || {
// SAFETY: Only called once during runtime initialization.
unsafe { init(argc, argv, sigpipe) };
let ret_code = panic::catch_unwind(main).unwrap_or_else(move |payload| {
// Carefully dispose of the panic payload.
let payload = panic::AssertUnwindSafe(payload);
panic::catch_unwind(move || drop({ payload }.0)).unwrap_or_else(move |e| {
mem::forget(e); // do *not* drop the 2nd payload
rtabort!("drop of the panic payload panicked");
});
// Return error code for panicking programs.
101
});
let ret_code = ret_code as isize;
cleanup();
// Guard against multiple threads calling `libc::exit` concurrently.
// See the documentation for `unique_thread_exit` for more information.
crate::sys::exit::unique_thread_exit();
ret_code
})
.unwrap_or_else(handle_rt_panic)
}
#[cfg(not(any(test, doctest)))]
#[lang = "start"]
fn lang_start<T: crate::process::Termination + 'static>(
main: fn() -> T,
argc: isize,
argv: *const *const u8,
sigpipe: u8,
) -> isize {
lang_start_internal(
&move || crate::sys::backtrace::__rust_begin_short_backtrace(main).report().to_i32(),
argc,
argv,
sigpipe,
)
}

View File

@@ -14,18 +14,19 @@ pub use core::sync::atomic;
pub use once::Once; pub use once::Once;
pub use once::OnceState; pub use once::OnceState;
pub use alloc_crate::sync::Arc;
pub use lazy_lock::LazyLock;
pub use once_lock::OnceLock;
pub use poison::Condvar;
pub use poison::LockResult; pub use poison::LockResult;
pub use poison::Mutex; pub use poison::Mutex;
pub use poison::MutexGuard; pub use poison::MutexGuard;
pub use poison::PoisonError; pub use poison::PoisonError;
pub use poison::RwLock;
pub use poison::TryLockError; pub use poison::TryLockError;
pub use poison::TryLockResult; pub use poison::TryLockResult;
pub use poison::Condvar;
pub use poison::RwLock;
pub use once_lock::OnceLock;
pub use reentrant_lock::ReentrantLock; pub use reentrant_lock::ReentrantLock;
pub use reentrant_lock::ReentrantLockGuard; pub use reentrant_lock::ReentrantLockGuard;
pub use alloc_crate::sync::Arc;
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[stable(feature = "wait_timeout", since = "1.5.0")] #[stable(feature = "wait_timeout", since = "1.5.0")]

View File

@@ -105,15 +105,6 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) } LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) }
} }
/// Creates a new lazy value that is already initialized.
#[inline]
#[cfg(test)]
pub(crate) fn preinit(value: T) -> LazyLock<T, F> {
let once = Once::new();
once.call_once(|| {});
LazyLock { once, data: UnsafeCell::new(Data { value: ManuallyDrop::new(value) }) }
}
/// Consumes this `LazyLock` returning the stored value. /// Consumes this `LazyLock` returning the stored value.
/// ///
/// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise. /// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise.
@@ -404,6 +395,19 @@ impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> {
} }
} }
#[stable(feature = "from_wrapper_impls", since = "CURRENT_RUSTC_VERSION")]
impl<T, F> From<T> for LazyLock<T, F> {
/// Constructs a `LazyLock` that starts already initialized
/// with the provided value.
#[inline]
fn from(value: T) -> Self {
LazyLock {
once: Once::new_complete(),
data: UnsafeCell::new(Data { value: ManuallyDrop::new(value) }),
}
}
}
#[cold] #[cold]
#[inline(never)] #[inline(never)]
fn panic_poisoned() -> ! { fn panic_poisoned() -> ! {

View File

@@ -4,7 +4,7 @@ use super::select::Selected;
use super::waker::current_thread_id; use super::waker::current_thread_id;
use crate::cell::Cell; use crate::cell::Cell;
use crate::ptr; use crate::ptr;
use alloc_crate::sync::Arc; use crate::sync::Arc;
use crate::sync::atomic::{Atomic, AtomicPtr, AtomicUsize, Ordering}; use crate::sync::atomic::{Atomic, AtomicPtr, AtomicUsize, Ordering};
use crate::thread::{self, Thread}; use crate::thread::{self, Thread};
use crate::time::Instant; use crate::time::Instant;

View File

@@ -84,6 +84,13 @@ impl Once {
Once { inner: sys::Once::new() } Once { inner: sys::Once::new() }
} }
/// Creates a new `Once` value that starts already completed.
#[inline]
#[must_use]
pub(crate) const fn new_complete() -> Once {
Once { inner: sys::Once::new_complete() }
}
/// Performs an initialization routine once and only once. The given closure /// Performs an initialization routine once and only once. The given closure
/// will be executed if this is the first time `call_once` has been called, /// will be executed if this is the first time `call_once` has been called,
/// and otherwise the routine will *not* be invoked. /// and otherwise the routine will *not* be invoked.

View File

@@ -0,0 +1,39 @@
//! Small helper functions used inside `sys`.
//!
//! If any of these have uses outside of `sys`, please move them to a different
//! module.
#[cfg_attr(not(target_os = "linux"), allow(unused))] // Not used on all platforms.
mod small_c_string;
#[cfg_attr(not(target_os = "windows"), allow(unused))] // Not used on all platforms.
mod wstr;
#[cfg(test)]
mod tests;
#[cfg_attr(not(target_os = "linux"), allow(unused))] // Not used on all platforms.
pub use small_c_string::{run_path_with_cstr, run_with_cstr};
#[cfg_attr(not(target_os = "windows"), allow(unused))] // Not used on all platforms.
pub use wstr::WStrUnits;
/// Computes `(value*numerator)/denom` without overflow, as long as both
/// `numerator*denom` and the overall result fit into `u64` (which is the case
/// for our time conversions).
#[cfg_attr(not(target_os = "windows"), allow(unused))] // Not used on all platforms.
pub fn mul_div_u64(value: u64, numerator: u64, denom: u64) -> u64 {
let q = value / denom;
let r = value % denom;
// Decompose value as (value/denom*denom + value%denom),
// substitute into (value*numerator)/denom and simplify.
// r < denom, so (denom*numerator) is the upper bound of (r*numerator)
q * numerator + r * numerator / denom
}
#[cfg_attr(not(target_os = "linux"), allow(unused))] // Not used on all platforms.
pub fn ignore_notfound<T>(result: crate::io::Result<T>) -> crate::io::Result<()> {
match result {
Err(err) if err.kind() == crate::io::ErrorKind::NotFound => Ok(()),
Ok(_) => Ok(()),
Err(err) => Err(err),
}
}

View File

@@ -0,0 +1,60 @@
use crate::ffi::{CStr, CString};
use crate::mem::MaybeUninit;
use crate::path::Path;
use crate::{io, ptr, slice};
// Make sure to stay under 4096 so the compiler doesn't insert a probe frame:
// https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
#[cfg(not(target_os = "espidf"))]
const MAX_STACK_ALLOCATION: usize = 384;
#[cfg(target_os = "espidf")]
const MAX_STACK_ALLOCATION: usize = 32;
const NUL_ERR: io::Error =
io::const_error!(io::ErrorKind::InvalidInput, "file name contained an unexpected NUL byte");
#[inline]
pub fn run_path_with_cstr<T>(path: &Path, f: &dyn Fn(&CStr) -> io::Result<T>) -> io::Result<T> {
run_with_cstr(path.as_os_str().as_encoded_bytes(), f)
}
#[inline]
pub fn run_with_cstr<T>(bytes: &[u8], f: &dyn Fn(&CStr) -> io::Result<T>) -> io::Result<T> {
// Dispatch and dyn erase the closure type to prevent mono bloat.
// See https://github.com/rust-lang/rust/pull/121101.
if bytes.len() >= MAX_STACK_ALLOCATION {
run_with_cstr_allocating(bytes, f)
} else {
unsafe { run_with_cstr_stack(bytes, f) }
}
}
/// # Safety
///
/// `bytes` must have a length less than `MAX_STACK_ALLOCATION`.
unsafe fn run_with_cstr_stack<T>(
bytes: &[u8],
f: &dyn Fn(&CStr) -> io::Result<T>,
) -> io::Result<T> {
let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
let buf_ptr = buf.as_mut_ptr() as *mut u8;
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len());
buf_ptr.add(bytes.len()).write(0);
}
match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) {
Ok(s) => f(s),
Err(_) => Err(NUL_ERR),
}
}
#[cold]
#[inline(never)]
fn run_with_cstr_allocating<T>(bytes: &[u8], f: &dyn Fn(&CStr) -> io::Result<T>) -> io::Result<T> {
match CString::new(bytes) {
Ok(s) => f(&s),
Err(_) => Err(NUL_ERR),
}
}

View File

@@ -0,0 +1,73 @@
use core::iter::repeat;
use super::mul_div_u64;
use super::small_c_string::run_path_with_cstr;
use crate::ffi::CString;
use crate::hint::black_box;
use crate::path::Path;
#[test]
fn stack_allocation_works() {
let path = Path::new("abc");
let result = run_path_with_cstr(path, &|p| {
assert_eq!(p, &*CString::new(path.as_os_str().as_encoded_bytes()).unwrap());
Ok(42)
});
assert_eq!(result.unwrap(), 42);
}
#[test]
fn stack_allocation_fails() {
let path = Path::new("ab\0");
assert!(run_path_with_cstr::<()>(path, &|_| unreachable!()).is_err());
}
#[test]
fn heap_allocation_works() {
let path = repeat("a").take(384).collect::<String>();
let path = Path::new(&path);
let result = run_path_with_cstr(path, &|p| {
assert_eq!(p, &*CString::new(path.as_os_str().as_encoded_bytes()).unwrap());
Ok(42)
});
assert_eq!(result.unwrap(), 42);
}
#[test]
fn heap_allocation_fails() {
let mut path = repeat("a").take(384).collect::<String>();
path.push('\0');
let path = Path::new(&path);
assert!(run_path_with_cstr::<()>(path, &|_| unreachable!()).is_err());
}
#[bench]
fn bench_stack_path_alloc(b: &mut test::Bencher) {
let path = repeat("a").take(383).collect::<String>();
let p = Path::new(&path);
b.iter(|| {
run_path_with_cstr(p, &|cstr| {
black_box(cstr);
Ok(())
})
.unwrap();
});
}
#[bench]
fn bench_heap_path_alloc(b: &mut test::Bencher) {
let path = repeat("a").take(384).collect::<String>();
let p = Path::new(&path);
b.iter(|| {
run_path_with_cstr(p, &|cstr| {
black_box(cstr);
Ok(())
})
.unwrap();
});
}
#[test]
fn test_muldiv() {
assert_eq!(mul_div_u64(1_000_000_000_001, 1_000_000_000, 1_000_000), 1_000_000_000_001_000);
}

View File

@@ -0,0 +1,59 @@
//! This module contains constructs to work with 16-bit characters (UCS-2 or UTF-16)
use crate::marker::PhantomData;
use crate::num::NonZero;
use crate::ptr::NonNull;
/// A safe iterator over a LPWSTR
/// (aka a pointer to a series of UTF-16 code units terminated by a NULL).
pub struct WStrUnits<'a> {
// The pointer must never be null...
lpwstr: NonNull<u16>,
// ...and the memory it points to must be valid for this lifetime.
lifetime: PhantomData<&'a [u16]>,
}
impl WStrUnits<'_> {
/// Creates the iterator. Returns `None` if `lpwstr` is null.
///
/// SAFETY: `lpwstr` must point to a null-terminated wide string that lives
/// at least as long as the lifetime of this struct.
pub unsafe fn new(lpwstr: *const u16) -> Option<Self> {
Some(Self { lpwstr: NonNull::new(lpwstr as _)?, lifetime: PhantomData })
}
pub fn peek(&self) -> Option<NonZero<u16>> {
// SAFETY: It's always safe to read the current item because we don't
// ever move out of the array's bounds.
unsafe { NonZero::new(*self.lpwstr.as_ptr()) }
}
/// Advance the iterator while `predicate` returns true.
/// Returns the number of items it advanced by.
pub fn advance_while<P: FnMut(NonZero<u16>) -> bool>(&mut self, mut predicate: P) -> usize {
let mut counter = 0;
while let Some(w) = self.peek() {
if !predicate(w) {
break;
}
counter += 1;
self.next();
}
counter
}
}
impl Iterator for WStrUnits<'_> {
// This can never return zero as that marks the end of the string.
type Item = NonZero<u16>;
fn next(&mut self) -> Option<Self::Item> {
// SAFETY: If NULL is reached we immediately return.
// Therefore it's safe to advance the pointer after that.
unsafe {
let next = self.peek()?;
self.lpwstr = NonNull::new_unchecked(self.lpwstr.as_ptr().add(1));
Some(next)
}
}
}

View File

@@ -19,7 +19,8 @@ pub mod thread;
pub mod thread_local; pub mod thread_local;
pub mod time; pub mod time;
pub use pal::*; pub use pal::*;
// pub mod fs; pub mod fs;
pub mod helpers;
/// A trait for viewing representations from std types. /// A trait for viewing representations from std types.
#[cfg_attr(not(target_os = "linux"), allow(unused))] #[cfg_attr(not(target_os = "linux"), allow(unused))]

View File

@@ -7,7 +7,7 @@ use crate::borrow::Cow;
use crate::bstr::ByteStr; use crate::bstr::ByteStr;
use alloc_crate::collections::TryReserveError; use alloc_crate::collections::TryReserveError;
use crate::rc::Rc; use crate::rc::Rc;
use alloc_crate::sync::Arc; use crate::sync::Arc;
use crate::sys::{AsInner, FromInner, IntoInner}; use crate::sys::{AsInner, FromInner, IntoInner};
use crate::{fmt, mem, str}; use crate::{fmt, mem, str};

View File

@@ -55,7 +55,3 @@ pub fn temp_dir() -> PathBuf {
pub fn home_dir() -> Option<PathBuf> { pub fn home_dir() -> Option<PathBuf> {
None None
} }
pub fn getpid() -> u32 {
panic!("no pids on this platform")
}

View File

@@ -1,3 +1,22 @@
// There's a lot of necessary redundancy in separator definition. Consolidated into a macro to
// prevent transcription errors.
macro_rules! path_separator_bytes {
($($sep:literal),+) => (
pub const SEPARATORS: &[char] = &[$($sep as char,)+];
pub const SEPARATORS_STR: &[&str] = &[$(
match str::from_utf8(&[$sep]) {
Ok(s) => s,
Err(_) => panic!("path_separator_bytes must be ASCII bytes"),
}
),+];
#[inline]
pub const fn is_sep_byte(b: u8) -> bool {
$(b == $sep) ||+
}
)
}
cfg_select! { cfg_select! {
target_os = "windows" => { target_os = "windows" => {
mod windows; mod windows;

View File

@@ -2,14 +2,11 @@ use crate::ffi::OsStr;
use crate::path::{Path, PathBuf, Prefix}; use crate::path::{Path, PathBuf, Prefix};
use crate::{env, io}; use crate::{env, io};
#[inline] path_separator_bytes!(b'/');
pub fn is_sep_byte(b: u8) -> bool {
b == b'/'
}
#[inline] #[inline]
pub fn is_verbatim_sep(b: u8) -> bool { pub const fn is_verbatim_sep(b: u8) -> bool {
b == b'/' is_sep_byte(b)
} }
#[inline] #[inline]
@@ -18,8 +15,6 @@ pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
} }
pub const HAS_PREFIXES: bool = false; pub const HAS_PREFIXES: bool = false;
pub const MAIN_SEP_STR: &str = "/";
pub const MAIN_SEP: char = '/';
/// Make a POSIX path absolute without changing its semantics. /// Make a POSIX path absolute without changing its semantics.
pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> { pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {

View File

@@ -27,9 +27,11 @@ cfg_select! {
mod env; mod env;
pub use env::CommandEnvs; pub use env::CommandEnvs;
#[cfg(target_family = "unix")]
pub use imp::getppid;
pub use imp::{ pub use imp::{
ChildPipe, Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio, ChildPipe, Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio,
read_output, getpid, read_output,
}; };
#[cfg(any( #[cfg(any(

View File

@@ -4,7 +4,7 @@ use crate::ffi::{OsStr, OsString};
use crate::num::NonZero; use crate::num::NonZero;
use crate::path::Path; use crate::path::Path;
use crate::process::StdioPipes; use crate::process::StdioPipes;
// use crate::sys::fs::File; use crate::sys::fs::File;
use crate::sys::unsupported; use crate::sys::unsupported;
use crate::{fmt, io}; use crate::{fmt, io};
@@ -30,8 +30,8 @@ pub enum Stdio {
MakePipe, MakePipe,
ParentStdout, ParentStdout,
ParentStderr, ParentStderr,
// #[allow(dead_code)] // This variant exists only for the Debug impl #[allow(dead_code)] // This variant exists only for the Debug impl
// InheritFile(File), InheritFile(File),
} }
impl Command { impl Command {
@@ -124,20 +124,18 @@ impl From<io::Stderr> for Stdio {
} }
} }
// impl From<File> for Stdio { impl From<File> for Stdio {
// fn from(file: File) -> Stdio { fn from(file: File) -> Stdio {
// Stdio::InheritFile(file) Stdio::InheritFile(file)
// } }
// } }
impl fmt::Debug for Command { impl fmt::Debug for Command {
// show all attributes // show all attributes
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() { if f.alternate() {
let mut debug_command = f.debug_struct("Command"); let mut debug_command = f.debug_struct("Command");
debug_command debug_command.field("program", &self.program).field("args", &self.args);
.field("program", &self.program)
.field("args", &self.args);
if !self.env.is_unchanged() { if !self.env.is_unchanged() {
debug_command.field("env", &self.env); debug_command.field("env", &self.env);
} }
@@ -329,3 +327,7 @@ pub fn read_output(
) -> io::Result<()> { ) -> io::Result<()> {
match out.diverge() {} match out.diverge() {}
} }
pub fn getpid() -> u32 {
panic!("no pids on this platform")
}

View File

@@ -12,7 +12,7 @@ cfg_select! {
all(target_os = "windows", not(target_vendor="win7")), all(target_os = "windows", not(target_vendor="win7")),
target_os = "linux", target_os = "linux",
target_os = "android", target_os = "android",
all(target_arch = "wasm32", target_feature = "atomics"), all(target_family = "wasm", target_feature = "atomics"),
target_os = "freebsd", target_os = "freebsd",
target_os = "motor", target_os = "motor",
target_os = "openbsd", target_os = "openbsd",

View File

@@ -39,6 +39,11 @@ impl Once {
Once { state: Cell::new(State::Incomplete) } Once { state: Cell::new(State::Incomplete) }
} }
#[inline]
pub const fn new_complete() -> Once {
Once { state: Cell::new(State::Complete) }
}
#[inline] #[inline]
pub fn is_completed(&self) -> bool { pub fn is_completed(&self) -> bool {
self.state.get() == State::Complete self.state.get() == State::Complete

View File

@@ -3,7 +3,7 @@ cfg_select! {
all(target_os = "windows", not(target_vendor = "win7")), all(target_os = "windows", not(target_vendor = "win7")),
target_os = "linux", target_os = "linux",
target_os = "android", target_os = "android",
all(target_arch = "wasm32", target_feature = "atomics"), all(target_family = "wasm", target_feature = "atomics"),
target_os = "freebsd", target_os = "freebsd",
target_os = "openbsd", target_os = "openbsd",
target_os = "dragonfly", target_os = "dragonfly",

View File

@@ -17,6 +17,7 @@ pub use current::current_id;
pub(crate) use current::current_or_unnamed; pub(crate) use current::current_or_unnamed;
pub(crate) use current::current_os_id; pub(crate) use current::current_os_id;
pub(crate) use current::with_current_name; pub(crate) use current::with_current_name;
pub(crate) use current::drop_current;
pub use functions::sleep; pub use functions::sleep;
pub use id::ThreadId; pub use id::ThreadId;
pub(crate) use lifecycle::ThreadInit; pub(crate) use lifecycle::ThreadInit;

View File

@@ -168,7 +168,11 @@ pub fn yield_now() {
imp::yield_now() imp::yield_now()
} }
/// Determines whether the current thread is unwinding because of panic. /// Determines whether the current thread is panicking.
///
/// This returns `true` both when the thread is unwinding due to a panic,
/// or executing a panic hook. Note that the latter case will still happen
/// when `panic=abort` is set.
/// ///
/// A common use of this feature is to poison shared resources when writing /// A common use of this feature is to poison shared resources when writing
/// unsafe code, by checking `panicking` when the `drop` is called. /// unsafe code, by checking `panicking` when the `drop` is called.
@@ -309,14 +313,14 @@ pub fn sleep(dur: Duration) {
/// ///
/// | Platform | System call | /// | Platform | System call |
/// |-----------|----------------------------------------------------------------------| /// |-----------|----------------------------------------------------------------------|
/// | Linux | [clock_nanosleep] (Monotonic clock) | /// | Linux | [clock_nanosleep] (Monotonic Clock) |
/// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock)] | /// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock) |
/// | Android | [clock_nanosleep] (Monotonic Clock)] | /// | Android | [clock_nanosleep] (Monotonic Clock) |
/// | Solaris | [clock_nanosleep] (Monotonic Clock)] | /// | Solaris | [clock_nanosleep] (Monotonic Clock) |
/// | Illumos | [clock_nanosleep] (Monotonic Clock)] | /// | Illumos | [clock_nanosleep] (Monotonic Clock) |
/// | Dragonfly | [clock_nanosleep] (Monotonic Clock)] | /// | Dragonfly | [clock_nanosleep] (Monotonic Clock) |
/// | Hurd | [clock_nanosleep] (Monotonic Clock)] | /// | Hurd | [clock_nanosleep] (Monotonic Clock) |
/// | Vxworks | [clock_nanosleep] (Monotonic Clock)] | /// | Vxworks | [clock_nanosleep] (Monotonic Clock) |
/// | Apple | `mach_wait_until` | /// | Apple | `mach_wait_until` |
/// | Other | `sleep_until` uses [`sleep`] and does not issue a syscall itself | /// | Other | `sleep_until` uses [`sleep`] and does not issue a syscall itself |
/// ///

View File

@@ -8,7 +8,7 @@ use super::{Result, spawnhook};
use crate::cell::UnsafeCell; use crate::cell::UnsafeCell;
use crate::marker::PhantomData; use crate::marker::PhantomData;
use crate::mem::{ManuallyDrop, MaybeUninit}; use crate::mem::{ManuallyDrop, MaybeUninit};
use alloc_crate::sync::Arc; use crate::sync::Arc;
use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
use crate::sys::{AsInner, IntoInner, thread as imp}; use crate::sys::{AsInner, IntoInner, thread as imp};
use crate::{env, io, panic}; use crate::{env, io, panic};

View File

@@ -5,7 +5,7 @@ use super::lifecycle::{JoinInner, spawn_unchecked};
use super::thread::Thread; use super::thread::Thread;
use crate::marker::PhantomData; use crate::marker::PhantomData;
use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind}; use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
use alloc_crate::sync::Arc; use crate::sync::Arc;
use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering}; use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering};
use crate::{fmt, io}; use crate::{fmt, io};

View File

@@ -1,7 +1,7 @@
use super::thread::Thread; use super::thread::Thread;
use crate::cell::Cell; use crate::cell::Cell;
use crate::iter; use crate::iter;
use alloc_crate::sync::Arc; use crate::sync::Arc;
crate::thread_local! { crate::thread_local! {
/// A thread local linked list of spawn hooks. /// A thread local linked list of spawn hooks.

View File

@@ -4,7 +4,7 @@ use crate::alloc::System;
use crate::ffi::CStr; use crate::ffi::CStr;
use crate::fmt; use crate::fmt;
use crate::pin::Pin; use crate::pin::Pin;
use alloc_crate::sync::Arc; use crate::sync::Arc;
use crate::sys::sync::Parker; use crate::sys::sync::Parker;
use crate::time::Duration; use crate::time::Duration;