Merge ref '828e45ad11' from rust-lang/rust

Pull recent changes from https://github.com/rust-lang/rust via Josh.

Upstream ref: 828e45ad11
Filtered ref: 10ab51e1b1b8eadb430163bd78ef39c0721cfbf8

This merge was created using https://github.com/rust-lang/josh-sync.
This commit is contained in:
The Miri Cronjob Bot 2025-09-01 05:02:58 +00:00
commit 4cc7ccaf98
99 changed files with 2688 additions and 993 deletions

View file

@ -315,7 +315,7 @@ pub enum TargetDataLayoutErrors<'a> {
MissingAlignment { cause: &'a str },
InvalidAlignment { cause: &'a str, err: AlignFromBytesError },
InconsistentTargetArchitecture { dl: &'a str, target: &'a str },
InconsistentTargetPointerWidth { pointer_size: u64, target: u32 },
InconsistentTargetPointerWidth { pointer_size: u64, target: u16 },
InvalidBitsSize { err: String },
UnknownPointerSpecification { err: String },
}

View file

@ -69,7 +69,7 @@ impl IntTy {
})
}
pub fn normalize(&self, target_width: u32) -> Self {
pub fn normalize(&self, target_width: u16) -> Self {
match self {
IntTy::Isize => match target_width {
16 => IntTy::I16,
@ -148,7 +148,7 @@ impl UintTy {
})
}
pub fn normalize(&self, target_width: u32) -> Self {
pub fn normalize(&self, target_width: u16) -> Self {
match self {
UintTy::Usize => match target_width {
16 => UintTy::U16,

View file

@ -58,7 +58,7 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
Allow(Target::AssocTy),
Allow(Target::AssocConst),
Allow(Target::Variant),
Allow(Target::Impl { of_trait: false }), //FIXME This does not make sense
Allow(Target::Impl { of_trait: false }),
Allow(Target::Crate),
Error(Target::WherePredicate),
]);

View file

@ -22,5 +22,5 @@
"unix"
],
"target-mcount": "_mcount",
"target-pointer-width": "32"
"target-pointer-width": 32
}

View file

@ -2309,10 +2309,10 @@ declare_lint! {
/// ### Example
///
/// ```rust
/// #![feature(sanitize)]
/// #![cfg_attr(not(bootstrap), feature(sanitize))]
///
/// #[inline(always)]
/// #[sanitize(address = "off")]
/// #[cfg_attr(not(bootstrap), sanitize(address = "off"))]
/// fn x() {}
///
/// fn main() {
@ -4832,13 +4832,16 @@ declare_lint! {
///
/// ### Example
///
/// ```rust,compile_fail
#[cfg_attr(not(bootstrap), doc = "```rust,compile_fail")]
#[cfg_attr(bootstrap, doc = "```rust")]
/// #![doc = in_root!()]
///
/// macro_rules! in_root { () => { "" } }
///
/// fn main() {}
/// ```
#[cfg_attr(not(bootstrap), doc = "```")]
#[cfg_attr(bootstrap, doc = "```")]
// ^ Needed to avoid tidy warning about odd number of backticks
///
/// {{produces}}
///

View file

@ -6,6 +6,7 @@
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(debug_closure_helpers)]
#![feature(default_field_values)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![recursion_limit = "256"]

View file

@ -6,6 +6,7 @@ use ast::Label;
use rustc_ast as ast;
use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind};
use rustc_ast::util::classify::{self, TrailingBrace};
use rustc_ast::visit::{Visitor, walk_expr};
use rustc_ast::{
AttrStyle, AttrVec, Block, BlockCheckMode, DUMMY_NODE_ID, Expr, ExprKind, HasAttrs, Local,
LocalKind, MacCall, MacCallStmt, MacStmtStyle, Recovered, Stmt, StmtKind,
@ -783,6 +784,100 @@ impl<'a> Parser<'a> {
Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
}
fn recover_missing_let_else(&mut self, err: &mut Diag<'_>, pat: &ast::Pat, stmt_span: Span) {
if self.token.kind != token::OpenBrace {
return;
}
match pat.kind {
ast::PatKind::Ident(..) | ast::PatKind::Missing | ast::PatKind::Wild => {
// Not if let or let else
return;
}
_ => {}
}
let snapshot = self.create_snapshot_for_diagnostic();
let block_span = self.token.span;
let (if_let, let_else) = match self.parse_block() {
Ok(block) => {
let mut idents = vec![];
pat.walk(&mut |pat: &ast::Pat| {
if let ast::PatKind::Ident(_, ident, _) = pat.kind {
idents.push(ident);
}
true
});
struct IdentFinder {
idents: Vec<Ident>,
/// If a block references one of the bindings introduced by the let pattern,
/// we likely meant to use `if let`.
/// This is pre-expansion, so if we encounter
/// `let Some(x) = foo() { println!("{x}") }` we won't find it.
references_ident: bool = false,
/// If a block has a `return`, then we know with high certainty that it was
/// meant to be let-else.
has_return: bool = false,
}
impl<'a> Visitor<'a> for IdentFinder {
fn visit_ident(&mut self, ident: &Ident) {
for i in &self.idents {
if ident.name == i.name {
self.references_ident = true;
}
}
}
fn visit_expr(&mut self, node: &'a Expr) {
if let ExprKind::Ret(..) = node.kind {
self.has_return = true;
}
walk_expr(self, node);
}
}
// Collect all bindings in pattern and see if they appear in the block. Likely meant
// to write `if let`. See if the block has a return. Likely meant to write
// `let else`.
let mut visitor = IdentFinder { idents, .. };
visitor.visit_block(&block);
(visitor.references_ident, visitor.has_return)
}
Err(e) => {
e.cancel();
self.restore_snapshot(snapshot);
(false, false)
}
};
let mut alternatively = "";
if if_let || !let_else {
alternatively = "alternatively, ";
err.span_suggestion_verbose(
stmt_span.shrink_to_lo(),
"you might have meant to use `if let`",
"if ".to_string(),
if if_let {
Applicability::MachineApplicable
} else {
Applicability::MaybeIncorrect
},
);
}
if let_else || !if_let {
err.span_suggestion_verbose(
block_span.shrink_to_lo(),
format!("{alternatively}you might have meant to use `let else`"),
"else ".to_string(),
if let_else {
Applicability::MachineApplicable
} else {
Applicability::MaybeIncorrect
},
);
}
}
fn recover_missing_dot(&mut self, err: &mut Diag<'_>) {
let Some((ident, _)) = self.token.ident() else {
return;
@ -977,6 +1072,7 @@ impl<'a> Parser<'a> {
self.check_mistyped_turbofish_with_multiple_type_params(e, expr).map_err(
|mut e| {
self.recover_missing_dot(&mut e);
self.recover_missing_let_else(&mut e, &local.pat, stmt.span);
e
},
)?;

View file

@ -858,7 +858,9 @@ impl<'input> Parser<'input> {
self.errors.insert(
0,
ParseError {
description: "expected format parameter to occur after `:`".to_owned(),
description:
"expected alignment specifier after `:` in format string; example: `{:>?}`"
.to_owned(),
note: None,
label: format!("expected `{}` to occur after `:`", alignment),
span: range,

View file

@ -0,0 +1,17 @@
use crate::spec::{RelroLevel, TargetOptions, cvs};
pub(crate) fn opts() -> TargetOptions {
TargetOptions {
os: "managarm".into(),
env: "mlibc".into(),
dynamic_linking: true,
executables: true,
families: cvs!["unix"],
has_rpath: true,
position_independent_executables: true,
relro_level: RelroLevel::Full,
has_thread_local: true,
crt_static_respected: true,
..Default::default()
}
}

View file

@ -20,6 +20,7 @@ pub(crate) mod linux_ohos;
pub(crate) mod linux_uclibc;
pub(crate) mod linux_wasm;
pub(crate) mod lynxos178;
pub(crate) mod managarm_mlibc;
pub(crate) mod msvc;
pub(crate) mod netbsd;
pub(crate) mod nto_qnx;

View file

@ -25,10 +25,7 @@ impl Target {
let mut base = Target {
llvm_target: json.llvm_target,
metadata: Default::default(),
pointer_width: json
.target_pointer_width
.parse()
.map_err(|err| format!("invalid target-pointer-width: {err}"))?,
pointer_width: json.target_pointer_width,
data_layout: json.data_layout,
arch: json.arch,
options: Default::default(),
@ -245,19 +242,17 @@ impl ToJson for Target {
target.update_to_cli();
macro_rules! target_val {
($attr:ident) => {{
let name = (stringify!($attr)).replace("_", "-");
d.insert(name, target.$attr.to_json());
($attr:ident) => {
target_val!($attr, (stringify!($attr)).replace("_", "-"))
};
($attr:ident, $json_name:expr) => {{
let name = $json_name;
d.insert(name.into(), target.$attr.to_json());
}};
}
macro_rules! target_option_val {
($attr:ident) => {{
let name = (stringify!($attr)).replace("_", "-");
if default.$attr != target.$attr {
d.insert(name, target.$attr.to_json());
}
}};
($attr:ident) => {{ target_option_val!($attr, (stringify!($attr)).replace("_", "-")) }};
($attr:ident, $json_name:expr) => {{
let name = $json_name;
if default.$attr != target.$attr {
@ -290,7 +285,7 @@ impl ToJson for Target {
target_val!(llvm_target);
target_val!(metadata);
d.insert("target-pointer-width".to_string(), self.pointer_width.to_string().to_json());
target_val!(pointer_width, "target-pointer-width");
target_val!(arch);
target_val!(data_layout);
@ -463,7 +458,7 @@ struct TargetSpecJsonMetadata {
#[serde(deny_unknown_fields)]
struct TargetSpecJson {
llvm_target: StaticCow<str>,
target_pointer_width: String,
target_pointer_width: u16,
data_layout: StaticCow<str>,
arch: StaticCow<str>,

View file

@ -2032,6 +2032,10 @@ supported_targets! {
("i586-unknown-redox", i586_unknown_redox),
("x86_64-unknown-redox", x86_64_unknown_redox),
("x86_64-unknown-managarm-mlibc", x86_64_unknown_managarm_mlibc),
("aarch64-unknown-managarm-mlibc", aarch64_unknown_managarm_mlibc),
("riscv64gc-unknown-managarm-mlibc", riscv64gc_unknown_managarm_mlibc),
("i386-apple-ios", i386_apple_ios),
("x86_64-apple-ios", x86_64_apple_ios),
("aarch64-apple-ios", aarch64_apple_ios),
@ -2327,7 +2331,7 @@ pub struct Target {
/// Used for generating target documentation.
pub metadata: TargetMetadata,
/// Number of bits in a pointer. Influences the `target_pointer_width` `cfg` variable.
pub pointer_width: u32,
pub pointer_width: u16,
/// Architecture to use for ABI considerations. Valid options include: "x86",
/// "x86_64", "arm", "aarch64", "mips", "powerpc", "powerpc64", and others.
pub arch: StaticCow<str>,

View file

@ -0,0 +1,22 @@
use crate::spec::{StackProbeType, Target, base};
pub(crate) fn target() -> Target {
let mut base = base::managarm_mlibc::opts();
base.max_atomic_width = Some(128);
base.stack_probes = StackProbeType::Inline;
base.features = "+v8a".into();
Target {
llvm_target: "aarch64-unknown-managarm-mlibc".into(),
metadata: crate::spec::TargetMetadata {
description: Some("managarm/aarch64".into()),
tier: Some(3),
host_tools: Some(false),
std: Some(false),
},
pointer_width: 64,
data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
arch: "aarch64".into(),
options: base
}
}

View file

@ -0,0 +1,24 @@
use crate::spec::{CodeModel, Target, TargetOptions, base};
pub(crate) fn target() -> Target {
Target {
llvm_target: "riscv64-unknown-managarm-mlibc".into(),
metadata: crate::spec::TargetMetadata {
description: Some("managarm/riscv64".into()),
tier: Some(3),
host_tools: Some(false),
std: Some(false),
},
pointer_width: 64,
data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
arch: "riscv64".into(),
options: TargetOptions {
code_model: Some(CodeModel::Medium),
cpu: "generic-rv64".into(),
features: "+m,+a,+f,+d,+c".into(),
llvm_abiname: "lp64d".into(),
max_atomic_width: Some(64),
..base::managarm_mlibc::opts()
},
}
}

View file

@ -0,0 +1,24 @@
use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, base};
pub(crate) fn target() -> Target {
let mut base = base::managarm_mlibc::opts();
base.cpu = "x86-64".into();
base.max_atomic_width = Some(64);
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
base.stack_probes = StackProbeType::Inline;
Target {
llvm_target: "x86_64-unknown-managarm-mlibc".into(),
metadata: crate::spec::TargetMetadata {
description: Some("managarm/amd64".into()),
tier: Some(3),
host_tools: Some(false),
std: Some(false),
},
pointer_width: 64,
data_layout:
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(),
arch: "x86_64".into(),
options: base,
}
}

View file

@ -7,7 +7,7 @@ fn report_unused_fields() {
"arch": "powerpc64",
"data-layout": "e-m:e-i64:64-n32:64",
"llvm-target": "powerpc64le-elf",
"target-pointer-width": "64",
"target-pointer-width": 64,
"code-mode": "foo"
}
"#;

View file

@ -354,7 +354,7 @@
//! sign := '+' | '-'
//! width := count
//! precision := count | '*'
//! type := '?' | 'x?' | 'X?' | identifier
//! type := '?' | 'x?' | 'X?' | 'o' | 'x' | 'X' | 'p' | 'b' | 'e' | 'E'
//! count := parameter | integer
//! parameter := argument '$'
//! ```

View file

@ -19,5 +19,5 @@
},
"panic-strategy": "abort",
"relocation-model": "static",
"target-pointer-width": "32"
"target-pointer-width": 32
}

View file

@ -41,8 +41,6 @@ pub use iter::IntoIter;
///
/// Creating multiple copies of a `String`:
/// ```rust
/// #![feature(array_repeat)]
///
/// use std::array;
///
/// let string = "Hello there!".to_string();
@ -50,7 +48,7 @@ pub use iter::IntoIter;
/// assert_eq!(strings, ["Hello there!", "Hello there!"]);
/// ```
#[inline]
#[unstable(feature = "array_repeat", issue = "126695")]
#[stable(feature = "array_repeat", since = "CURRENT_RUSTC_VERSION")]
pub fn repeat<T: Clone, const N: usize>(val: T) -> [T; N] {
from_trusted_iterator(repeat_n(val, N))
}

View file

@ -950,7 +950,11 @@ impl char {
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn is_control(self) -> bool {
unicode::Cc(self)
// According to
// https://www.unicode.org/policies/stability_policy.html#Property_Value,
// the set of codepoints in `Cc` will never change.
// So we can just hard-code the patterns to match against instead of using a table.
matches!(self, '\0'..='\x1f' | '\x7f'..='\u{9f}')
}
/// Returns `true` if this `char` has the `Grapheme_Extend` property.

View file

@ -776,12 +776,45 @@ pub fn select_unpredictable<T>(condition: bool, true_val: T, false_val: T) -> T
// Change this to use ManuallyDrop instead.
let mut true_val = MaybeUninit::new(true_val);
let mut false_val = MaybeUninit::new(false_val);
struct DropOnPanic<T> {
// Invariant: valid pointer and points to an initialized value that is not further used,
// i.e. it can be dropped by this guard.
inner: *mut T,
}
impl<T> Drop for DropOnPanic<T> {
fn drop(&mut self) {
// SAFETY: Must be guaranteed on construction of local type `DropOnPanic`.
unsafe { self.inner.drop_in_place() }
}
}
let true_ptr = true_val.as_mut_ptr();
let false_ptr = false_val.as_mut_ptr();
// SAFETY: The value that is not selected is dropped, and the selected one
// is returned. This is necessary because the intrinsic doesn't drop the
// value that is not selected.
unsafe {
crate::intrinsics::select_unpredictable(!condition, &mut true_val, &mut false_val)
.assume_init_drop();
// Extract the selected value first, ensure it is dropped as well if dropping the unselected
// value panics. We construct a temporary by-pointer guard around the selected value while
// dropping the unselected value. Arguments overlap here, so we can not use mutable
// reference for these arguments.
let guard = crate::intrinsics::select_unpredictable(condition, true_ptr, false_ptr);
let drop = crate::intrinsics::select_unpredictable(condition, false_ptr, true_ptr);
// SAFETY: both pointers are well-aligned and point to initialized values inside a
// `MaybeUninit` each. In both possible values for `condition` the pointer `guard` and
// `drop` do not alias (even though the two argument pairs we have selected from did alias
// each other).
let guard = DropOnPanic { inner: guard };
drop.drop_in_place();
crate::mem::forget(guard);
// Note that it is important to use the values here. Reading from the pointer we got makes
// LLVM forget the !unpredictable annotation sometimes (in tests, integer sized values in
// particular seemed to confuse it, also observed in llvm/llvm-project #82340).
crate::intrinsics::select_unpredictable(condition, true_val, false_val).assume_init()
}
}

View file

@ -1491,6 +1491,20 @@ macro_rules! uint_impl {
without modifying the original"]
#[inline]
pub const fn checked_ilog(self, base: Self) -> Option<u32> {
// Inform compiler of optimizations when the base is known at
// compile time and there's a cheaper method available.
//
// Note: Like all optimizations, this is not guaranteed to be
// applied by the compiler. If you want those specific bases,
// use `.checked_ilog2()` or `.checked_ilog10()` directly.
if core::intrinsics::is_val_statically_known(base) {
if base == 2 {
return self.checked_ilog2();
} else if base == 10 {
return self.checked_ilog10();
}
}
if self <= 0 || base <= 1 {
None
} else if self < base {
@ -2447,7 +2461,7 @@ macro_rules! uint_impl {
}
/// Calculates `self` + `rhs` + `carry` and returns a tuple containing
/// the sum and the output carry.
/// the sum and the output carry (in that order).
///
/// Performs "ternary addition" of two integer operands and a carry-in
/// bit, and returns an output integer and a carry-out bit. This allows
@ -2465,8 +2479,6 @@ macro_rules! uint_impl {
/// # Examples
///
/// ```
/// #![feature(bigint_helper_methods)]
///
#[doc = concat!("// 3 MAX (a = 3 × 2^", stringify!($BITS), " + 2^", stringify!($BITS), " - 1)")]
#[doc = concat!("// + 5 7 (b = 5 × 2^", stringify!($BITS), " + 7)")]
/// // ---------
@ -2483,7 +2495,7 @@ macro_rules! uint_impl {
///
/// assert_eq!((sum1, sum0), (9, 6));
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[stable(feature = "unsigned_bigint_helpers", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
@ -2559,8 +2571,6 @@ macro_rules! uint_impl {
/// # Examples
///
/// ```
/// #![feature(bigint_helper_methods)]
///
#[doc = concat!("// 9 6 (a = 9 × 2^", stringify!($BITS), " + 6)")]
#[doc = concat!("// - 5 7 (b = 5 × 2^", stringify!($BITS), " + 7)")]
/// // ---------
@ -2577,7 +2587,7 @@ macro_rules! uint_impl {
///
#[doc = concat!("assert_eq!((diff1, diff0), (3, ", stringify!($SelfT), "::MAX));")]
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[stable(feature = "unsigned_bigint_helpers", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
@ -2651,10 +2661,12 @@ macro_rules! uint_impl {
/// indicating whether an arithmetic overflow would occur. If an
/// overflow would have occurred then the wrapped value is returned.
///
/// If you want the *value* of the overflow, rather than just *whether*
/// an overflow occurred, see [`Self::carrying_mul`].
///
/// # Examples
///
/// Please note that this example is shared among integer types, which is why why `u32`
/// is used.
/// Please note that this example is shared among integer types, which is why `u32` is used.
///
/// ```
/// assert_eq!(5u32.overflowing_mul(2), (10, false));
@ -2670,16 +2682,38 @@ macro_rules! uint_impl {
(a as Self, b)
}
/// Calculates the complete product `self * rhs` without the possibility to overflow.
/// Calculates the complete double-width product `self * rhs`.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
/// of the result as two separate values, in that order. As such,
/// `a.widening_mul(b).0` produces the same result as `a.wrapping_mul(b)`.
///
/// If you also need to add a value and carry to the wide result, then you want
/// [`Self::carrying_mul_add`] instead.
///
/// If you also need to add a carry to the wide result, then you want
/// [`Self::carrying_mul`] instead.
///
/// If you just want to know *whether* the multiplication overflowed, then you
/// want [`Self::overflowing_mul`] instead.
///
/// # Examples
///
/// ```
/// #![feature(bigint_helper_methods)]
#[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".widening_mul(7), (35, 0));")]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.widening_mul(", stringify!($SelfT), "::MAX), (1, ", stringify!($SelfT), "::MAX - 1));")]
/// ```
///
/// Compared to other `*_mul` methods:
/// ```
/// #![feature(bigint_helper_methods)]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::widening_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), (0, 3));")]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::overflowing_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), (0, true));")]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::wrapping_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), 0);")]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::checked_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), None);")]
/// ```
///
/// Please note that this example is shared among integer types, which is why `u32` is used.
///
/// ```
@ -2706,14 +2740,13 @@ macro_rules! uint_impl {
/// additional amount of overflow. This allows for chaining together multiple
/// multiplications to create "big integers" which represent larger values.
///
/// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead.
/// If you also need to add a value, then use [`Self::carrying_mul_add`].
///
/// # Examples
///
/// Please note that this example is shared among integer types, which is why `u32` is used.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5u32.carrying_mul(2, 0), (10, 0));
/// assert_eq!(5u32.carrying_mul(2, 10), (20, 0));
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2));
@ -2771,7 +2804,7 @@ macro_rules! uint_impl {
/// 789_u16.wrapping_mul(456).wrapping_add(123),
/// );
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[stable(feature = "unsigned_bigint_helpers", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
@ -2780,18 +2813,20 @@ macro_rules! uint_impl {
Self::carrying_mul_add(self, rhs, carry, 0)
}
/// Calculates the "full multiplication" `self * rhs + carry1 + carry2`
/// without the possibility to overflow.
/// Calculates the "full multiplication" `self * rhs + carry1 + carry2`.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
///
/// This cannot overflow, as the double-width result has exactly enough
/// space for the largest possible result. This is equivalent to how, in
/// decimal, 9 × 9 + 9 + 9 = 81 + 18 = 99 = 9×10⁰ + 9×10¹ = 10² - 1.
///
/// Performs "long multiplication" which takes in an extra amount to add, and may return an
/// additional amount of overflow. This allows for chaining together multiple
/// multiplications to create "big integers" which represent larger values.
///
/// If you don't need either `carry`, then you can use [`Self::widening_mul`] instead,
/// and if you only need one `carry`, then you can use [`Self::carrying_mul`] instead.
/// If you don't need the `add` part, then you can use [`Self::carrying_mul`] instead.
///
/// # Examples
///
@ -2799,7 +2834,6 @@ macro_rules! uint_impl {
/// which explains why `u32` is used here.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5u32.carrying_mul_add(2, 0, 0), (10, 0));
/// assert_eq!(5u32.carrying_mul_add(2, 10, 10), (30, 0));
/// assert_eq!(1_000_000_000u32.carrying_mul_add(10, 0, 0), (1410065408, 2));
@ -2816,8 +2850,6 @@ macro_rules! uint_impl {
/// using `u8` for simplicity of the demonstration.
///
/// ```
/// #![feature(bigint_helper_methods)]
///
/// fn quadratic_mul<const N: usize>(a: [u8; N], b: [u8; N]) -> [u8; N] {
/// let mut out = [0; N];
/// for j in 0..N {
@ -2832,13 +2864,13 @@ macro_rules! uint_impl {
/// // -1 * -1 == 1
/// assert_eq!(quadratic_mul([0xFF; 3], [0xFF; 3]), [1, 0, 0]);
///
/// assert_eq!(u32::wrapping_mul(0x9e3779b9, 0x7f4a7c15), 0xCFFC982D);
/// assert_eq!(u32::wrapping_mul(0x9e3779b9, 0x7f4a7c15), 0xcffc982d);
/// assert_eq!(
/// quadratic_mul(u32::to_le_bytes(0x9e3779b9), u32::to_le_bytes(0x7f4a7c15)),
/// u32::to_le_bytes(0xCFFC982D)
/// u32::to_le_bytes(0xcffc982d)
/// );
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[stable(feature = "unsigned_bigint_helpers", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]

View file

@ -2166,10 +2166,9 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
}
}
/// Align pointer `p`.
/// Calculate an element-offset that increases a pointer's alignment.
///
/// Calculate offset (in terms of elements of `size_of::<T>()` stride) that has to be applied
/// to pointer `p` so that pointer `p` would get aligned to `a`.
/// Calculate an element-offset (not byte-offset) that when added to a given pointer `p`, increases `p`'s alignment to at least the given alignment `a`.
///
/// # Safety
/// `a` must be a power of two.

View file

@ -308,6 +308,42 @@ impl Duration {
Duration { secs, nanos: subsec_nanos }
}
/// Creates a new `Duration` from the specified number of nanoseconds.
///
/// # Panics
///
/// Panics if the given number of nanoseconds is greater than [`Duration::MAX`].
///
/// # Examples
///
/// ```
/// #![feature(duration_from_nanos_u128)]
/// use std::time::Duration;
///
/// let nanos = 10_u128.pow(24) + 321;
/// let duration = Duration::from_nanos_u128(nanos);
///
/// assert_eq!(10_u64.pow(15), duration.as_secs());
/// assert_eq!(321, duration.subsec_nanos());
/// ```
#[unstable(feature = "duration_from_nanos_u128", issue = "139201")]
// This is necessary because of const `try_from`, but can be removed if a trait-free impl is used instead
#[rustc_const_unstable(feature = "duration_from_nanos_u128", issue = "139201")]
#[must_use]
#[inline]
#[track_caller]
pub const fn from_nanos_u128(nanos: u128) -> Duration {
const NANOS_PER_SEC: u128 = self::NANOS_PER_SEC as u128;
let Ok(secs) = u64::try_from(nanos / NANOS_PER_SEC) else {
panic!("overflow in `Duration::from_nanos_u128`");
};
let subsec_nanos = (nanos % NANOS_PER_SEC) as u32;
// SAFETY: x % 1_000_000_000 < 1_000_000_000 also, subsec_nanos >= 0 since u128 >=0 and u32 >=0
let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_nanos) };
Duration { secs: secs as u64, nanos: subsec_nanos }
}
/// Creates a new `Duration` from the specified number of weeks.
///
/// # Panics

View file

@ -10,7 +10,6 @@ pub use unicode_data::conversions;
#[rustfmt::skip]
pub(crate) use unicode_data::alphabetic::lookup as Alphabetic;
pub(crate) use unicode_data::cc::lookup as Cc;
pub(crate) use unicode_data::grapheme_extend::lookup as Grapheme_Extend;
pub(crate) use unicode_data::lowercase::lookup as Lowercase;
pub(crate) use unicode_data::n::lookup as N;

View file

@ -358,31 +358,6 @@ pub mod cased {
}
}
#[rustfmt::skip]
pub mod cc {
use super::ShortOffsetRunHeader;
static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 1] = [
ShortOffsetRunHeader::new(0, 1114272),
];
static OFFSETS: [u8; 5] = [
0, 32, 95, 33, 0,
];
pub fn lookup(c: char) -> bool {
const {
assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32);
let mut i = 0;
while i < SHORT_OFFSET_RUNS.len() {
assert!(SHORT_OFFSET_RUNS[i].start_index() < OFFSETS.len());
i += 1;
}
}
// SAFETY: We just ensured the last element of `SHORT_OFFSET_RUNS` is greater than `std::char::MAX`
// and the start indices of all elements in `SHORT_OFFSET_RUNS` are smaller than `OFFSETS.len()`.
unsafe { super::skip_search(c, &SHORT_OFFSET_RUNS, &OFFSETS) }
}
}
#[rustfmt::skip]
pub mod grapheme_extend {
use super::ShortOffsetRunHeader;

View file

@ -21,3 +21,39 @@ fn select_unpredictable_drop() {
assert!(a_dropped.get());
assert!(b_dropped.get());
}
#[test]
#[should_panic = "message canary"]
fn select_unpredictable_drop_on_panic() {
use core::cell::Cell;
struct X<'a> {
cell: &'a Cell<u16>,
expect: u16,
write: u16,
}
impl Drop for X<'_> {
fn drop(&mut self) {
let value = self.cell.get();
self.cell.set(self.write);
assert_eq!(value, self.expect, "message canary");
}
}
let cell = Cell::new(0);
// Trigger a double-panic if the selected cell was not dropped during panic.
let _armed = X { cell: &cell, expect: 0xdead, write: 0 };
let selected = X { cell: &cell, write: 0xdead, expect: 1 };
let unselected = X { cell: &cell, write: 1, expect: 0xff };
// The correct drop order is:
//
// 1. `unselected` drops, writes 1, and panics as 0 != 0xff
// 2. `selected` drops during unwind, writes 0xdead and does not panic as 1 == 1
// 3. `armed` drops during unwind, writes 0 and does not panic as 0xdead == 0xdead
//
// If `selected` is not dropped, `armed` panics as 1 != 0xdead
let _unreachable = core::hint::select_unpredictable(true, selected, unselected);
}

View file

@ -36,6 +36,7 @@
#![feature(drop_guard)]
#![feature(duration_constants)]
#![feature(duration_constructors)]
#![feature(duration_from_nanos_u128)]
#![feature(error_generic_member_access)]
#![feature(exact_div)]
#![feature(exact_size_is_empty)]

View file

@ -45,6 +45,14 @@ fn from_weeks_overflow() {
let _ = Duration::from_weeks(overflow);
}
#[test]
#[should_panic]
fn from_nanos_u128_overflow() {
let nanos_per_sec: u128 = 1_000_000_000;
let overflow = (u64::MAX as u128 * nanos_per_sec) + (nanos_per_sec - 1) + 1;
let _ = Duration::from_nanos_u128(overflow);
}
#[test]
fn constructor_weeks() {
assert_eq!(Duration::from_weeks(1), Duration::from_secs(7 * 24 * 60 * 60));
@ -81,6 +89,8 @@ fn secs() {
assert_eq!(Duration::from_micros(1_000_001).as_secs(), 1);
assert_eq!(Duration::from_nanos(999_999_999).as_secs(), 0);
assert_eq!(Duration::from_nanos(1_000_000_001).as_secs(), 1);
assert_eq!(Duration::from_nanos_u128(999_999_999).as_secs(), 0);
assert_eq!(Duration::from_nanos_u128(1_000_000_001).as_secs(), 1);
}
#[test]
@ -95,6 +105,8 @@ fn millis() {
assert_eq!(Duration::from_micros(1_001_000).subsec_millis(), 1);
assert_eq!(Duration::from_nanos(999_999_999).subsec_millis(), 999);
assert_eq!(Duration::from_nanos(1_001_000_000).subsec_millis(), 1);
assert_eq!(Duration::from_nanos_u128(999_999_999).subsec_millis(), 999);
assert_eq!(Duration::from_nanos_u128(1_001_000_001).subsec_millis(), 1);
}
#[test]
@ -109,6 +121,8 @@ fn micros() {
assert_eq!(Duration::from_micros(1_000_001).subsec_micros(), 1);
assert_eq!(Duration::from_nanos(999_999_999).subsec_micros(), 999_999);
assert_eq!(Duration::from_nanos(1_000_001_000).subsec_micros(), 1);
assert_eq!(Duration::from_nanos_u128(999_999_999).subsec_micros(), 999_999);
assert_eq!(Duration::from_nanos_u128(1_000_001_000).subsec_micros(), 1);
}
#[test]
@ -123,6 +137,8 @@ fn nanos() {
assert_eq!(Duration::from_micros(1_000_001).subsec_nanos(), 1000);
assert_eq!(Duration::from_nanos(999_999_999).subsec_nanos(), 999_999_999);
assert_eq!(Duration::from_nanos(1_000_000_001).subsec_nanos(), 1);
assert_eq!(Duration::from_nanos_u128(999_999_999).subsec_nanos(), 999_999_999);
assert_eq!(Duration::from_nanos_u128(1_000_000_001).subsec_nanos(), 1);
}
#[test]
@ -520,6 +536,9 @@ fn duration_const() {
const FROM_NANOS: Duration = Duration::from_nanos(1_000_000_000);
assert_eq!(FROM_NANOS, Duration::SECOND);
const FROM_NANOS_U128: Duration = Duration::from_nanos_u128(NANOS);
assert_eq!(FROM_NANOS_U128, Duration::SECOND);
const MAX: Duration = Duration::new(u64::MAX, 999_999_999);
const CHECKED_ADD: Option<Duration> = MAX.checked_add(Duration::SECOND);

View file

@ -304,7 +304,7 @@ pub struct DirBuilder {
pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
fn inner(path: &Path) -> io::Result<Vec<u8>> {
let mut file = File::open(path)?;
let size = file.metadata().map(|m| m.len() as usize).ok();
let size = file.metadata().map(|m| usize::try_from(m.len()).unwrap_or(usize::MAX)).ok();
let mut bytes = Vec::try_with_capacity(size.unwrap_or(0))?;
io::default_read_to_end(&mut file, &mut bytes, size)?;
Ok(bytes)
@ -346,7 +346,7 @@ pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
fn inner(path: &Path) -> io::Result<String> {
let mut file = File::open(path)?;
let size = file.metadata().map(|m| m.len() as usize).ok();
let size = file.metadata().map(|m| usize::try_from(m.len()).unwrap_or(usize::MAX)).ok();
let mut string = String::new();
string.try_reserve_exact(size.unwrap_or(0))?;
io::default_read_to_string(&mut file, &mut string, size)?;
@ -1614,6 +1614,10 @@ impl OpenOptions {
/// See also [`std::fs::write()`][self::write] for a simple function to
/// create a file with some given data.
///
/// # Errors
///
/// If `.create(true)` is set without `.write(true)` or `.append(true)`,
/// calling [`open`](Self::open) will fail with [`InvalidInput`](io::ErrorKind::InvalidInput) error.
/// # Examples
///
/// ```no_run
@ -1685,7 +1689,8 @@ impl OpenOptions {
/// * [`AlreadyExists`]: `create_new` was specified and the file already
/// exists.
/// * [`InvalidInput`]: Invalid combinations of open options (truncate
/// without write access, no access mode set, etc.).
/// without write access, create without write or append access,
/// no access mode set, etc.).
///
/// The following errors don't match any existing [`io::ErrorKind`] at the moment:
/// * One of the directory components of the specified file path

View file

@ -1265,12 +1265,7 @@ fn open_flavors() {
let mut ra = OO::new();
ra.read(true).append(true);
#[cfg(windows)]
let invalid_options = 87; // ERROR_INVALID_PARAMETER
#[cfg(all(unix, not(target_os = "vxworks")))]
let invalid_options = "Invalid argument";
#[cfg(target_os = "vxworks")]
let invalid_options = "invalid argument";
let invalid_options = "creating or truncating a file requires write or append access";
// Test various combinations of creation modes and access modes.
//
@ -1293,10 +1288,10 @@ fn open_flavors() {
check!(c(&w).open(&tmpdir.join("a")));
// read-only
error!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options);
error!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options);
error!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options);
error!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options);
error_contains!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options);
error_contains!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options);
error_contains!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options);
error_contains!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options);
check!(c(&r).open(&tmpdir.join("a"))); // try opening the file created with write_only
// read-write
@ -1308,21 +1303,21 @@ fn open_flavors() {
// append
check!(c(&a).create_new(true).open(&tmpdir.join("d")));
error!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options);
error!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options);
error_contains!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options);
error_contains!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options);
check!(c(&a).create(true).open(&tmpdir.join("d")));
check!(c(&a).open(&tmpdir.join("d")));
// read-append
check!(c(&ra).create_new(true).open(&tmpdir.join("e")));
error!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options);
error!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options);
error_contains!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options);
error_contains!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options);
check!(c(&ra).create(true).open(&tmpdir.join("e")));
check!(c(&ra).open(&tmpdir.join("e")));
// Test opening a file without setting an access mode
let mut blank = OO::new();
error!(blank.create(true).open(&tmpdir.join("f")), invalid_options);
error_contains!(blank.create(true).open(&tmpdir.join("f")), invalid_options);
// Test write works
check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes()));
@ -2084,3 +2079,34 @@ fn test_rename_junction() {
// Junction links are always absolute so we just check the file name is correct.
assert_eq!(fs::read_link(&dest).unwrap().file_name(), Some(not_exist.as_os_str()));
}
#[test]
fn test_open_options_invalid_combinations() {
use crate::fs::OpenOptions as OO;
let test_cases: &[(fn() -> OO, &str)] = &[
(|| OO::new().create(true).read(true).clone(), "create without write"),
(|| OO::new().create_new(true).read(true).clone(), "create_new without write"),
(|| OO::new().truncate(true).read(true).clone(), "truncate without write"),
(|| OO::new().truncate(true).append(true).clone(), "truncate with append"),
];
for (make_opts, desc) in test_cases {
let opts = make_opts();
let result = opts.open("nonexistent.txt");
assert!(result.is_err(), "{desc} should fail");
let err = result.unwrap_err();
assert_eq!(err.kind(), ErrorKind::InvalidInput, "{desc} - wrong error kind");
assert_eq!(
err.to_string(),
"creating or truncating a file requires write or append access",
"{desc} - wrong error message"
);
}
let result = OO::new().open("nonexistent.txt");
assert!(result.is_err(), "no access mode should fail");
let err = result.unwrap_err();
assert_eq!(err.kind(), ErrorKind::InvalidInput);
assert_eq!(err.to_string(), "must specify at least one of read, write, or append access");
}

View file

@ -1,6 +1,5 @@
use crate::fmt;
// FIXME(nonpoison_mutex,nonpoison_condvar): switch to nonpoison versions once they are available
use crate::sync::{Condvar, Mutex};
use crate::sync::nonpoison::{Condvar, Mutex};
/// A barrier enables multiple threads to synchronize the beginning
/// of some computation.
@ -118,12 +117,11 @@ impl Barrier {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn wait(&self) -> BarrierWaitResult {
let mut lock = self.lock.lock().unwrap();
let mut lock = self.lock.lock();
let local_gen = lock.generation_id;
lock.count += 1;
if lock.count < self.num_threads {
let _guard =
self.cvar.wait_while(lock, |state| local_gen == state.generation_id).unwrap();
let _guard = self.cvar.wait_while(lock, |state| local_gen == state.generation_id);
BarrierWaitResult(false)
} else {
lock.count = 0;

View file

@ -209,7 +209,7 @@ pub use self::poison::{LockResult, PoisonError};
#[doc(inline)]
pub use self::poison::{
Mutex, MutexGuard, TryLockError, TryLockResult,
Condvar, WaitTimeoutResult,
Condvar,
Once, OnceState,
RwLock, RwLockReadGuard, RwLockWriteGuard,
};
@ -234,3 +234,66 @@ mod barrier;
mod lazy_lock;
mod once_lock;
mod reentrant_lock;
/// A type indicating whether a timed wait on a condition variable returned
/// due to a time out or not.
///
/// It is returned by the [`wait_timeout`] method.
///
/// [`wait_timeout`]: Condvar::wait_timeout
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[stable(feature = "wait_timeout", since = "1.5.0")]
pub struct WaitTimeoutResult(bool);
impl WaitTimeoutResult {
/// Returns `true` if the wait was known to have timed out.
///
/// # Examples
///
/// This example spawns a thread which will sleep 20 milliseconds before
/// updating a boolean value and then notifying the condvar.
///
/// The main thread will wait with a 10 millisecond timeout on the condvar
/// and will leave the loop upon timeout.
///
/// ```
/// use std::sync::{Arc, Condvar, Mutex};
/// use std::thread;
/// use std::time::Duration;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
/// let pair2 = Arc::clone(&pair);
///
/// # let handle =
/// thread::spawn(move || {
/// let (lock, cvar) = &*pair2;
///
/// // Let's wait 20 milliseconds before notifying the condvar.
/// thread::sleep(Duration::from_millis(20));
///
/// let mut started = lock.lock().unwrap();
/// // We update the boolean value.
/// *started = true;
/// cvar.notify_one();
/// });
///
/// // Wait for the thread to start up.
/// let (lock, cvar) = &*pair;
/// loop {
/// // Let's put a timeout on the condvar's wait.
/// let result = cvar.wait_timeout(lock.lock().unwrap(), Duration::from_millis(10)).unwrap();
/// // 10 milliseconds have passed.
/// if result.1.timed_out() {
/// // timed out now and we can leave.
/// break
/// }
/// }
/// # // Prevent leaks for Miri.
/// # let _ = handle.join();
/// ```
#[must_use]
#[stable(feature = "wait_timeout", since = "1.5.0")]
pub fn timed_out(&self) -> bool {
self.0
}
}

View file

@ -29,6 +29,8 @@ impl fmt::Display for WouldBlock {
}
}
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
pub use self::condvar::Condvar;
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
pub use self::mutex::MappedMutexGuard;
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
@ -38,5 +40,6 @@ pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard};
#[unstable(feature = "nonpoison_rwlock", issue = "134645")]
pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
mod condvar;
mod mutex;
mod rwlock;

View file

@ -0,0 +1,448 @@
use crate::fmt;
use crate::sync::WaitTimeoutResult;
use crate::sync::nonpoison::{MutexGuard, mutex};
use crate::sys::sync as sys;
use crate::time::{Duration, Instant};
/// A Condition Variable
///
/// For more information about condition variables, check out the documentation for the poisoning
/// variant of this type at [`poison::Condvar`].
///
/// # Examples
///
/// Note that this `Condvar` does **not** propagate information about threads that panic while
/// holding a lock. If you need this functionality, see [`poison::Mutex`] and [`poison::Condvar`].
///
/// ```
/// #![feature(nonpoison_mutex)]
/// #![feature(nonpoison_condvar)]
///
/// use std::sync::nonpoison::{Mutex, Condvar};
/// use std::sync::Arc;
/// use std::thread;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
/// let pair2 = Arc::clone(&pair);
///
/// // Inside of our lock, spawn a new thread, and then wait for it to start.
/// thread::spawn(move || {
/// let (lock, cvar) = &*pair2;
/// let mut started = lock.lock();
/// *started = true;
/// // We notify the condvar that the value has changed.
/// cvar.notify_one();
/// });
///
/// // Wait for the thread to start up.
/// let (lock, cvar) = &*pair;
/// let mut started = lock.lock();
/// while !*started {
/// started = cvar.wait(started);
/// }
/// ```
///
/// [`poison::Mutex`]: crate::sync::poison::Mutex
/// [`poison::Condvar`]: crate::sync::poison::Condvar
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
pub struct Condvar {
inner: sys::Condvar,
}
impl Condvar {
/// Creates a new condition variable which is ready to be waited on and
/// notified.
///
/// # Examples
///
/// ```
/// use std::sync::Condvar;
///
/// let condvar = Condvar::new();
/// ```
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
#[must_use]
#[inline]
pub const fn new() -> Condvar {
Condvar { inner: sys::Condvar::new() }
}
/// Blocks the current thread until this condition variable receives a
/// notification.
///
/// This function will atomically unlock the mutex specified (represented by
/// `guard`) and block the current thread. This means that any calls
/// to [`notify_one`] or [`notify_all`] which happen logically after the
/// mutex is unlocked are candidates to wake this thread up. When this
/// function call returns, the lock specified will have been re-acquired.
///
/// Note that this function is susceptible to spurious wakeups. Condition
/// variables normally have a boolean predicate associated with them, and
/// the predicate must always be checked each time this function returns to
/// protect against spurious wakeups.
///
/// # Panics
///
/// This function may [`panic!`] if it is used with more than one mutex
/// over time.
///
/// [`notify_one`]: Self::notify_one
/// [`notify_all`]: Self::notify_all
///
/// # Examples
///
/// ```
/// #![feature(nonpoison_mutex)]
/// #![feature(nonpoison_condvar)]
///
/// use std::sync::nonpoison::{Mutex, Condvar};
/// use std::sync::Arc;
/// use std::thread;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
/// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move || {
/// let (lock, cvar) = &*pair2;
/// let mut started = lock.lock();
/// *started = true;
/// // We notify the condvar that the value has changed.
/// cvar.notify_one();
/// });
///
/// // Wait for the thread to start up.
/// let (lock, cvar) = &*pair;
/// let mut started = lock.lock();
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
/// while !*started {
/// started = cvar.wait(started);
/// }
/// ```
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
unsafe {
let lock = mutex::guard_lock(&guard);
self.inner.wait(lock);
}
guard
}
/// Blocks the current thread until the provided condition becomes false.
///
/// `condition` is checked immediately; if not met (returns `true`), this
/// will [`wait`] for the next notification then check again. This repeats
/// until `condition` returns `false`, in which case this function returns.
///
/// This function will atomically unlock the mutex specified (represented by
/// `guard`) and block the current thread. This means that any calls
/// to [`notify_one`] or [`notify_all`] which happen logically after the
/// mutex is unlocked are candidates to wake this thread up. When this
/// function call returns, the lock specified will have been re-acquired.
///
/// [`wait`]: Self::wait
/// [`notify_one`]: Self::notify_one
/// [`notify_all`]: Self::notify_all
///
/// # Examples
///
/// ```
/// #![feature(nonpoison_mutex)]
/// #![feature(nonpoison_condvar)]
///
/// use std::sync::nonpoison::{Mutex, Condvar};
/// use std::sync::Arc;
/// use std::thread;
///
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
/// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move || {
/// let (lock, cvar) = &*pair2;
/// let mut pending = lock.lock();
/// *pending = false;
/// // We notify the condvar that the value has changed.
/// cvar.notify_one();
/// });
///
/// // Wait for the thread to start up.
/// let (lock, cvar) = &*pair;
/// // As long as the value inside the `Mutex<bool>` is `true`, we wait.
/// let _guard = cvar.wait_while(lock.lock(), |pending| { *pending });
/// ```
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
pub fn wait_while<'a, T, F>(
&self,
mut guard: MutexGuard<'a, T>,
mut condition: F,
) -> MutexGuard<'a, T>
where
F: FnMut(&mut T) -> bool,
{
while condition(&mut *guard) {
guard = self.wait(guard);
}
guard
}
/// Waits on this condition variable for a notification, timing out after a
/// specified duration.
///
/// The semantics of this function are equivalent to [`wait`] except that
/// the thread will be blocked for roughly no longer than `dur`. This
/// method should not be used for precise timing due to anomalies such as
/// preemption or platform differences that might not cause the maximum
/// amount of time waited to be precisely `dur`.
///
/// Note that the best effort is made to ensure that the time waited is
/// measured with a monotonic clock, and not affected by the changes made to
/// the system time. This function is susceptible to spurious wakeups.
/// Condition variables normally have a boolean predicate associated with
/// them, and the predicate must always be checked each time this function
/// returns to protect against spurious wakeups. Additionally, it is
/// typically desirable for the timeout to not exceed some duration in
/// spite of spurious wakes, thus the sleep-duration is decremented by the
/// amount slept. Alternatively, use the `wait_timeout_while` method
/// to wait with a timeout while a predicate is true.
///
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
/// known to have elapsed.
///
/// Like [`wait`], the lock specified will be re-acquired when this function
/// returns, regardless of whether the timeout elapsed or not.
///
/// [`wait`]: Self::wait
/// [`wait_timeout_while`]: Self::wait_timeout_while
///
/// # Examples
///
/// ```
/// #![feature(nonpoison_mutex)]
/// #![feature(nonpoison_condvar)]
///
/// use std::sync::nonpoison::{Mutex, Condvar};
/// use std::sync::Arc;
/// use std::thread;
/// use std::time::Duration;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
/// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move || {
/// let (lock, cvar) = &*pair2;
/// let mut started = lock.lock();
/// *started = true;
/// // We notify the condvar that the value has changed.
/// cvar.notify_one();
/// });
///
/// // wait for the thread to start up
/// let (lock, cvar) = &*pair;
/// let mut started = lock.lock();
/// // as long as the value inside the `Mutex<bool>` is `false`, we wait
/// loop {
/// let result = cvar.wait_timeout(started, Duration::from_millis(10));
/// // 10 milliseconds have passed, or maybe the value changed!
/// started = result.0;
/// if *started == true {
/// // We received the notification and the value has been updated, we can leave.
/// break
/// }
/// }
/// ```
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
pub fn wait_timeout<'a, T>(
&self,
guard: MutexGuard<'a, T>,
dur: Duration,
) -> (MutexGuard<'a, T>, WaitTimeoutResult) {
let success = unsafe {
let lock = mutex::guard_lock(&guard);
self.inner.wait_timeout(lock, dur)
};
(guard, WaitTimeoutResult(!success))
}
/// Waits on this condition variable for a notification, timing out after a
/// specified duration.
///
/// The semantics of this function are equivalent to [`wait_while`] except
/// that the thread will be blocked for roughly no longer than `dur`. This
/// method should not be used for precise timing due to anomalies such as
/// preemption or platform differences that might not cause the maximum
/// amount of time waited to be precisely `dur`.
///
/// Note that the best effort is made to ensure that the time waited is
/// measured with a monotonic clock, and not affected by the changes made to
/// the system time.
///
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
/// known to have elapsed without the condition being met.
///
/// Like [`wait_while`], the lock specified will be re-acquired when this
/// function returns, regardless of whether the timeout elapsed or not.
///
/// [`wait_while`]: Self::wait_while
/// [`wait_timeout`]: Self::wait_timeout
///
/// # Examples
///
/// ```
/// #![feature(nonpoison_mutex)]
/// #![feature(nonpoison_condvar)]
///
/// use std::sync::nonpoison::{Mutex, Condvar};
/// use std::sync::Arc;
/// use std::thread;
/// use std::time::Duration;
///
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
/// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move || {
/// let (lock, cvar) = &*pair2;
/// let mut pending = lock.lock();
/// *pending = false;
/// // We notify the condvar that the value has changed.
/// cvar.notify_one();
/// });
///
/// // wait for the thread to start up
/// let (lock, cvar) = &*pair;
/// let result = cvar.wait_timeout_while(
/// lock.lock(),
/// Duration::from_millis(100),
/// |&mut pending| pending,
/// );
/// if result.1.timed_out() {
/// // timed-out without the condition ever evaluating to false.
/// }
/// // access the locked mutex via result.0
/// ```
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
pub fn wait_timeout_while<'a, T, F>(
&self,
mut guard: MutexGuard<'a, T>,
dur: Duration,
mut condition: F,
) -> (MutexGuard<'a, T>, WaitTimeoutResult)
where
F: FnMut(&mut T) -> bool,
{
let start = Instant::now();
loop {
if !condition(&mut *guard) {
return (guard, WaitTimeoutResult(false));
}
let timeout = match dur.checked_sub(start.elapsed()) {
Some(timeout) => timeout,
None => return (guard, WaitTimeoutResult(true)),
};
guard = self.wait_timeout(guard, timeout).0;
}
}
/// Wakes up one blocked thread on this condvar.
///
/// If there is a blocked thread on this condition variable, then it will
/// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to
/// `notify_one` are not buffered in any way.
///
/// To wake up all threads, see [`notify_all`].
///
/// [`wait`]: Self::wait
/// [`wait_timeout`]: Self::wait_timeout
/// [`notify_all`]: Self::notify_all
///
/// # Examples
///
/// ```
/// #![feature(nonpoison_mutex)]
/// #![feature(nonpoison_condvar)]
///
/// use std::sync::nonpoison::{Mutex, Condvar};
/// use std::sync::Arc;
/// use std::thread;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
/// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move || {
/// let (lock, cvar) = &*pair2;
/// let mut started = lock.lock();
/// *started = true;
/// // We notify the condvar that the value has changed.
/// cvar.notify_one();
/// });
///
/// // Wait for the thread to start up.
/// let (lock, cvar) = &*pair;
/// let mut started = lock.lock();
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
/// while !*started {
/// started = cvar.wait(started);
/// }
/// ```
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
pub fn notify_one(&self) {
self.inner.notify_one()
}
/// Wakes up all blocked threads on this condvar.
///
/// This method will ensure that any current waiters on the condition
/// variable are awoken. Calls to `notify_all()` are not buffered in any
/// way.
///
/// To wake up only one thread, see [`notify_one`].
///
/// [`notify_one`]: Self::notify_one
///
/// # Examples
///
/// ```
/// #![feature(nonpoison_mutex)]
/// #![feature(nonpoison_condvar)]
///
/// use std::sync::nonpoison::{Mutex, Condvar};
/// use std::sync::Arc;
/// use std::thread;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
/// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move || {
/// let (lock, cvar) = &*pair2;
/// let mut started = lock.lock();
/// *started = true;
/// // We notify the condvar that the value has changed.
/// cvar.notify_all();
/// });
///
/// // Wait for the thread to start up.
/// let (lock, cvar) = &*pair;
/// let mut started = lock.lock();
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
/// while !*started {
/// started = cvar.wait(started);
/// }
/// ```
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
pub fn notify_all(&self) {
self.inner.notify_all()
}
}
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
impl fmt::Debug for Condvar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Condvar").finish_non_exhaustive()
}
}
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
impl Default for Condvar {
/// Creates a `Condvar` which is ready to be waited on and notified.
fn default() -> Condvar {
Condvar::new()
}
}

View file

@ -114,7 +114,6 @@ impl<T: ?Sized> !Send for MutexGuard<'_, T> {}
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
// FIXME(nonpoison_condvar): Use this link instead: [`Condvar`]: crate::sync::nonpoison::Condvar
/// An RAII mutex guard returned by `MutexGuard::map`, which can point to a
/// subfield of the protected data. When this structure is dropped (falls out
/// of scope), the lock will be unlocked.
@ -131,7 +130,7 @@ unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
///
/// [`map`]: MutexGuard::map
/// [`filter_map`]: MutexGuard::filter_map
/// [`Condvar`]: crate::sync::Condvar
/// [`Condvar`]: crate::sync::nonpoison::Condvar
#[must_use = "if unused the Mutex will immediately unlock"]
#[must_not_suspend = "holding a MappedMutexGuard across suspend \
points can cause deadlocks, delays, \
@ -458,6 +457,11 @@ impl<T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'_, T> {
}
}
/// For use in [`nonpoison::condvar`](super::condvar).
pub(super) fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex {
&guard.lock.inner
}
impl<'a, T: ?Sized> MutexGuard<'a, T> {
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g.
/// an enum variant.

View file

@ -61,7 +61,7 @@
//! then the lock will not be poisoned.
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::condvar::{Condvar, WaitTimeoutResult};
pub use self::condvar::Condvar;
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
pub use self::mutex::MappedMutexGuard;
#[stable(feature = "rust1", since = "1.0.0")]

View file

@ -1,73 +1,9 @@
use crate::fmt;
use crate::sync::WaitTimeoutResult;
use crate::sync::poison::{self, LockResult, MutexGuard, PoisonError, mutex};
use crate::sys::sync as sys;
use crate::time::{Duration, Instant};
/// A type indicating whether a timed wait on a condition variable returned
/// due to a time out or not.
///
/// It is returned by the [`wait_timeout`] method.
///
/// [`wait_timeout`]: Condvar::wait_timeout
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[stable(feature = "wait_timeout", since = "1.5.0")]
pub struct WaitTimeoutResult(bool);
// FIXME(nonpoison_condvar): `WaitTimeoutResult` is actually poisoning-agnostic, it seems.
// Should we take advantage of this fact?
impl WaitTimeoutResult {
/// Returns `true` if the wait was known to have timed out.
///
/// # Examples
///
/// This example spawns a thread which will sleep 20 milliseconds before
/// updating a boolean value and then notifying the condvar.
///
/// The main thread will wait with a 10 millisecond timeout on the condvar
/// and will leave the loop upon timeout.
///
/// ```
/// use std::sync::{Arc, Condvar, Mutex};
/// use std::thread;
/// use std::time::Duration;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
/// let pair2 = Arc::clone(&pair);
///
/// # let handle =
/// thread::spawn(move || {
/// let (lock, cvar) = &*pair2;
///
/// // Let's wait 20 milliseconds before notifying the condvar.
/// thread::sleep(Duration::from_millis(20));
///
/// let mut started = lock.lock().unwrap();
/// // We update the boolean value.
/// *started = true;
/// cvar.notify_one();
/// });
///
/// // Wait for the thread to start up.
/// let (lock, cvar) = &*pair;
/// loop {
/// // Let's put a timeout on the condvar's wait.
/// let result = cvar.wait_timeout(lock.lock().unwrap(), Duration::from_millis(10)).unwrap();
/// // 10 milliseconds have passed.
/// if result.1.timed_out() {
/// // timed out now and we can leave.
/// break
/// }
/// }
/// # // Prevent leaks for Miri.
/// # let _ = handle.join();
/// ```
#[must_use]
#[stable(feature = "wait_timeout", since = "1.5.0")]
pub fn timed_out(&self) -> bool {
self.0
}
}
/// A Condition Variable
///
/// Condition variables represent the ability to block a thread such that it

View file

@ -757,11 +757,13 @@ impl<T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'_, T> {
}
}
pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex {
/// For use in [`nonpoison::condvar`](super::condvar).
pub(super) fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex {
&guard.lock.inner
}
pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag {
/// For use in [`nonpoison::condvar`](super::condvar).
pub(super) fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag {
&guard.lock.poison
}

View file

@ -1123,7 +1123,21 @@ impl OpenOptions {
(true, true, false) => Ok(libc::O_RDWR),
(false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
(true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
(false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
(false, false, false) => {
// If no access mode is set, check if any creation flags are set
// to provide a more descriptive error message
if self.create || self.create_new || self.truncate {
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"creating or truncating a file requires write or append access",
))
} else {
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"must specify at least one of read, write, or append access",
))
}
}
}
}
@ -1132,12 +1146,18 @@ impl OpenOptions {
(true, false) => {}
(false, false) => {
if self.truncate || self.create || self.create_new {
return Err(Error::from_raw_os_error(libc::EINVAL));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"creating or truncating a file requires write or append access",
));
}
}
(_, true) => {
if self.truncate && !self.create_new {
return Err(Error::from_raw_os_error(libc::EINVAL));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"creating or truncating a file requires write or append access",
));
}
}
}

View file

@ -258,7 +258,19 @@ impl OpenOptions {
Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA))
}
(false, false, false, None) => {
Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32))
// If no access mode is set, check if any creation flags are set
// to provide a more descriptive error message
if self.create || self.create_new || self.truncate {
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"creating or truncating a file requires write or append access",
))
} else {
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"must specify at least one of read, write, or append access",
))
}
}
}
}
@ -268,12 +280,18 @@ impl OpenOptions {
(true, false) => {}
(false, false) => {
if self.truncate || self.create || self.create_new {
return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"creating or truncating a file requires write or append access",
));
}
}
(_, true) => {
if self.truncate && !self.create_new {
return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"creating or truncating a file requires write or append access",
));
}
}
}

View file

@ -186,14 +186,24 @@ pub fn chdir(p: &path::Path) -> io::Result<()> {
if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) }
}
pub type SplitPaths<'a> = impl Iterator<Item = PathBuf>;
// This can't just be `impl Iterator` because that requires `'a` to be live on
// drop (see #146045).
pub type SplitPaths<'a> = iter::Map<
slice::Split<'a, u8, impl FnMut(&u8) -> bool + 'static>,
impl FnMut(&[u8]) -> PathBuf + 'static,
>;
#[define_opaque(SplitPaths)]
pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
unparsed
.as_bytes()
.split(|&b| b == PATH_SEPARATOR)
.map(|part| PathBuf::from(OsStr::from_bytes(part)))
fn is_separator(&b: &u8) -> bool {
b == PATH_SEPARATOR
}
fn into_pathbuf(part: &[u8]) -> PathBuf {
PathBuf::from(OsStr::from_bytes(part))
}
unparsed.as_bytes().split(is_separator).map(into_pathbuf)
}
#[derive(Debug)]

View file

@ -1,7 +1,8 @@
use super::{Arg, make_command_line};
use crate::env;
use crate::ffi::{OsStr, OsString};
use crate::process::Command;
use crate::os::windows::io::AsHandle;
use crate::process::{Command, Stdio};
#[test]
fn test_raw_args() {
@ -29,19 +30,30 @@ fn test_thread_handle() {
use crate::os::windows::process::{ChildExt, CommandExt};
const CREATE_SUSPENDED: u32 = 0x00000004;
let p = Command::new("cmd").args(&["/C", "exit 0"]).creation_flags(CREATE_SUSPENDED).spawn();
let p = Command::new("whoami").stdout(Stdio::null()).creation_flags(CREATE_SUSPENDED).spawn();
assert!(p.is_ok());
let mut p = p.unwrap();
// Ensure the process is killed in the event something goes wrong.
struct DropGuard(crate::process::Child);
impl Drop for DropGuard {
fn drop(&mut self) {
let _ = self.0.kill();
}
}
let mut p = DropGuard(p.unwrap());
let p = &mut p.0;
unsafe extern "system" {
fn ResumeThread(_: BorrowedHandle<'_>) -> u32;
unsafe fn ResumeThread(hHandle: BorrowedHandle<'_>) -> u32;
unsafe fn WaitForSingleObject(hHandle: BorrowedHandle<'_>, dwMilliseconds: u32) -> u32;
}
unsafe {
ResumeThread(p.main_thread_handle());
// Wait until the process exits or 1 minute passes.
// We don't bother checking the result here as that's done below using `try_wait`.
WaitForSingleObject(p.as_handle(), 1000 * 60);
}
crate::thread::sleep(crate::time::Duration::from_millis(100));
let res = p.try_wait();
assert!(res.is_ok());
assert!(res.unwrap().is_some());

View file

@ -1,190 +1,269 @@
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::channel;
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
use std::time::Duration;
#[test]
fn smoke() {
let c = Condvar::new();
c.notify_one();
c.notify_all();
}
use super::nonpoison_and_poison_unwrap_test;
#[test]
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
fn notify_one() {
let m = Arc::new(Mutex::new(()));
let m2 = m.clone();
let c = Arc::new(Condvar::new());
let c2 = c.clone();
nonpoison_and_poison_unwrap_test!(
name: smoke,
test_body: {
use locks::Condvar;
let g = m.lock().unwrap();
let _t = thread::spawn(move || {
let _g = m2.lock().unwrap();
c2.notify_one();
});
let g = c.wait(g).unwrap();
drop(g);
}
#[test]
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
fn notify_all() {
const N: usize = 10;
let data = Arc::new((Mutex::new(0), Condvar::new()));
let (tx, rx) = channel();
for _ in 0..N {
let data = data.clone();
let tx = tx.clone();
thread::spawn(move || {
let &(ref lock, ref cond) = &*data;
let mut cnt = lock.lock().unwrap();
*cnt += 1;
if *cnt == N {
tx.send(()).unwrap();
}
while *cnt != 0 {
cnt = cond.wait(cnt).unwrap();
}
tx.send(()).unwrap();
});
let c = Condvar::new();
c.notify_one();
c.notify_all();
}
drop(tx);
);
let &(ref lock, ref cond) = &*data;
rx.recv().unwrap();
let mut cnt = lock.lock().unwrap();
*cnt = 0;
cond.notify_all();
drop(cnt);
for _ in 0..N {
rx.recv().unwrap();
}
}
#[test]
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
fn wait_while() {
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = pair.clone();
nonpoison_and_poison_unwrap_test!(
name: notify_one,
test_body: {
use locks::{Condvar, Mutex};
// Inside of our lock, spawn a new thread, and then wait for it to start.
thread::spawn(move || {
let &(ref lock, ref cvar) = &*pair2;
let mut started = lock.lock().unwrap();
*started = true;
// We notify the condvar that the value has changed.
cvar.notify_one();
});
// Wait for the thread to start up.
let &(ref lock, ref cvar) = &*pair;
let guard = cvar.wait_while(lock.lock().unwrap(), |started| !*started);
assert!(*guard.unwrap());
}
#[test]
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported
fn wait_timeout_wait() {
let m = Arc::new(Mutex::new(()));
let c = Arc::new(Condvar::new());
loop {
let g = m.lock().unwrap();
let (_g, no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap();
// spurious wakeups mean this isn't necessarily true
// so execute test again, if not timeout
if !no_timeout.timed_out() {
continue;
}
break;
}
}
#[test]
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported
fn wait_timeout_while_wait() {
let m = Arc::new(Mutex::new(()));
let c = Arc::new(Condvar::new());
let g = m.lock().unwrap();
let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(1), |_| true).unwrap();
// no spurious wakeups. ensure it timed-out
assert!(wait.timed_out());
}
#[test]
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported
fn wait_timeout_while_instant_satisfy() {
let m = Arc::new(Mutex::new(()));
let c = Arc::new(Condvar::new());
let g = m.lock().unwrap();
let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(0), |_| false).unwrap();
// ensure it didn't time-out even if we were not given any time.
assert!(!wait.timed_out());
}
#[test]
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
fn wait_timeout_while_wake() {
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair_copy = pair.clone();
let &(ref m, ref c) = &*pair;
let g = m.lock().unwrap();
let _t = thread::spawn(move || {
let &(ref lock, ref cvar) = &*pair_copy;
let mut started = lock.lock().unwrap();
thread::sleep(Duration::from_millis(1));
*started = true;
cvar.notify_one();
});
let (g2, wait) = c
.wait_timeout_while(g, Duration::from_millis(u64::MAX), |&mut notified| !notified)
.unwrap();
// ensure it didn't time-out even if we were not given any time.
assert!(!wait.timed_out());
assert!(*g2);
}
#[test]
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
fn wait_timeout_wake() {
let m = Arc::new(Mutex::new(()));
let c = Arc::new(Condvar::new());
loop {
let g = m.lock().unwrap();
let c2 = c.clone();
let m = Arc::new(Mutex::new(()));
let m2 = m.clone();
let c = Arc::new(Condvar::new());
let c2 = c.clone();
let notified = Arc::new(AtomicBool::new(false));
let notified_copy = notified.clone();
let t = thread::spawn(move || {
let _g = m2.lock().unwrap();
thread::sleep(Duration::from_millis(1));
notified_copy.store(true, Ordering::Relaxed);
let g = maybe_unwrap(m.lock());
let _t = thread::spawn(move || {
let _g = maybe_unwrap(m2.lock());
c2.notify_one();
});
let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap();
assert!(!timeout_res.timed_out());
// spurious wakeups mean this isn't necessarily true
// so execute test again, if not notified
if !notified.load(Ordering::Relaxed) {
t.join().unwrap();
continue;
}
let g = maybe_unwrap(c.wait(g));
drop(g);
t.join().unwrap();
break;
}
}
);
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
nonpoison_and_poison_unwrap_test!(
name: notify_all,
test_body: {
use locks::{Condvar, Mutex};
const N: usize = 10;
let data = Arc::new((Mutex::new(0), Condvar::new()));
let (tx, rx) = channel();
for _ in 0..N {
let data = data.clone();
let tx = tx.clone();
thread::spawn(move || {
let &(ref lock, ref cond) = &*data;
let mut cnt = maybe_unwrap(lock.lock());
*cnt += 1;
if *cnt == N {
tx.send(()).unwrap();
}
while *cnt != 0 {
cnt = maybe_unwrap(cond.wait(cnt));
}
tx.send(()).unwrap();
});
}
drop(tx);
let &(ref lock, ref cond) = &*data;
rx.recv().unwrap();
let mut cnt = maybe_unwrap(lock.lock());
*cnt = 0;
cond.notify_all();
drop(cnt);
for _ in 0..N {
rx.recv().unwrap();
}
}
);
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
nonpoison_and_poison_unwrap_test!(
name: test_mutex_arc_condvar,
test_body: {
use locks::{Condvar, Mutex};
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
let packet2 = Packet(packet.0.clone());
let (tx, rx) = channel();
let _t = thread::spawn(move || {
// Wait until our parent has taken the lock.
rx.recv().unwrap();
let &(ref lock, ref cvar) = &*packet2.0;
// Set the data to `true` and wake up our parent.
let mut guard = maybe_unwrap(lock.lock());
*guard = true;
cvar.notify_one();
});
let &(ref lock, ref cvar) = &*packet.0;
let mut guard = maybe_unwrap(lock.lock());
// Wake up our child.
tx.send(()).unwrap();
// Wait until our child has set the data to `true`.
assert!(!*guard);
while !*guard {
guard = maybe_unwrap(cvar.wait(guard));
}
}
);
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
nonpoison_and_poison_unwrap_test!(
name: wait_while,
test_body: {
use locks::{Condvar, Mutex};
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = pair.clone();
// Inside of our lock, spawn a new thread, and then wait for it to start.
thread::spawn(move || {
let &(ref lock, ref cvar) = &*pair2;
let mut started = maybe_unwrap(lock.lock());
*started = true;
// We notify the condvar that the value has changed.
cvar.notify_one();
});
// Wait for the thread to start up.
let &(ref lock, ref cvar) = &*pair;
let guard = cvar.wait_while(maybe_unwrap(lock.lock()), |started| !*started);
assert!(*maybe_unwrap(guard));
}
);
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
nonpoison_and_poison_unwrap_test!(
name: wait_timeout_wait,
test_body: {
use locks::{Condvar, Mutex};
let m = Arc::new(Mutex::new(()));
let c = Arc::new(Condvar::new());
loop {
let g = maybe_unwrap(m.lock());
let (_g, no_timeout) = maybe_unwrap(c.wait_timeout(g, Duration::from_millis(1)));
// spurious wakeups mean this isn't necessarily true
// so execute test again, if not timeout
if !no_timeout.timed_out() {
continue;
}
break;
}
}
);
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
nonpoison_and_poison_unwrap_test!(
name: wait_timeout_while_wait,
test_body: {
use locks::{Condvar, Mutex};
let m = Arc::new(Mutex::new(()));
let c = Arc::new(Condvar::new());
let g = maybe_unwrap(m.lock());
let (_g, wait) = maybe_unwrap(c.wait_timeout_while(g, Duration::from_millis(1), |_| true));
// no spurious wakeups. ensure it timed-out
assert!(wait.timed_out());
}
);
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
nonpoison_and_poison_unwrap_test!(
name: wait_timeout_while_instant_satisfy,
test_body: {
use locks::{Condvar, Mutex};
let m = Arc::new(Mutex::new(()));
let c = Arc::new(Condvar::new());
let g = maybe_unwrap(m.lock());
let (_g, wait) =
maybe_unwrap(c.wait_timeout_while(g, Duration::from_millis(0), |_| false));
// ensure it didn't time-out even if we were not given any time.
assert!(!wait.timed_out());
}
);
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
nonpoison_and_poison_unwrap_test!(
name: wait_timeout_while_wake,
test_body: {
use locks::{Condvar, Mutex};
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair_copy = pair.clone();
let &(ref m, ref c) = &*pair;
let g = maybe_unwrap(m.lock());
let _t = thread::spawn(move || {
let &(ref lock, ref cvar) = &*pair_copy;
let mut started = maybe_unwrap(lock.lock());
thread::sleep(Duration::from_millis(1));
*started = true;
cvar.notify_one();
});
let (g2, wait) = maybe_unwrap(c.wait_timeout_while(
g,
Duration::from_millis(u64::MAX),
|&mut notified| !notified
));
// ensure it didn't time-out even if we were not given any time.
assert!(!wait.timed_out());
assert!(*g2);
}
);
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
nonpoison_and_poison_unwrap_test!(
name: wait_timeout_wake,
test_body: {
use locks::{Condvar, Mutex};
let m = Arc::new(Mutex::new(()));
let c = Arc::new(Condvar::new());
loop {
let g = maybe_unwrap(m.lock());
let c2 = c.clone();
let m2 = m.clone();
let notified = Arc::new(AtomicBool::new(false));
let notified_copy = notified.clone();
let t = thread::spawn(move || {
let _g = maybe_unwrap(m2.lock());
thread::sleep(Duration::from_millis(1));
notified_copy.store(true, Ordering::Relaxed);
c2.notify_one();
});
let (g, timeout_res) =
maybe_unwrap(c.wait_timeout(g, Duration::from_millis(u64::MAX)));
assert!(!timeout_res.timed_out());
// spurious wakeups mean this isn't necessarily true
// so execute test again, if not notified
if !notified.load(Ordering::Relaxed) {
t.join().unwrap();
continue;
}
drop(g);
t.join().unwrap();
break;
}
}
);

View file

@ -7,6 +7,7 @@
#![feature(rwlock_downgrade)]
#![feature(std_internals)]
#![feature(sync_nonpoison)]
#![feature(nonpoison_condvar)]
#![feature(nonpoison_mutex)]
#![feature(nonpoison_rwlock)]
#![allow(internal_features)]

View file

@ -213,40 +213,6 @@ nonpoison_and_poison_unwrap_test!(
}
);
// FIXME(nonpoison_condvar): Move this to the `condvar.rs` test file once `nonpoison::condvar` gets
// implemented.
#[test]
fn test_mutex_arc_condvar() {
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
let packet2 = Packet(packet.0.clone());
let (tx, rx) = channel();
let _t = thread::spawn(move || {
// Wait until our parent has taken the lock.
rx.recv().unwrap();
let &(ref lock, ref cvar) = &*packet2.0;
// Set the data to `true` and wake up our parent.
let mut guard = lock.lock().unwrap();
*guard = true;
cvar.notify_one();
});
let &(ref lock, ref cvar) = &*packet.0;
let mut guard = lock.lock().unwrap();
// Wake up our child.
tx.send(()).unwrap();
// Wait until our child has set the data to `true`.
assert!(!*guard);
while !*guard {
guard = cvar.wait(guard).unwrap();
}
}
nonpoison_and_poison_unwrap_test!(
name: test_mutex_arc_nested,
test_body: {

4
package-lock.json generated
View file

@ -5,7 +5,7 @@
"packages": {
"": {
"dependencies": {
"browser-ui-test": "^0.21.1",
"browser-ui-test": "^0.21.3",
"es-check": "^6.2.1",
"eslint": "^8.57.1",
"eslint-js": "github:eslint/js",
@ -555,7 +555,7 @@
}
},
"node_modules/browser-ui-test": {
"version": "0.21.1",
"version": "0.21.3",
"resolved": "https://registry.npmjs.org/browser-ui-test/-/browser-ui-test-0.21.1.tgz",
"integrity": "sha512-b3a9mhALAmbP+GifoN/c295RPyuyfIUFMz0DtlBHbeDW5PYQTMHZZJtm7R2UyP6JiIQSkR+7227sS3maMGMUTg==",
"license": "MIT",

View file

@ -1,6 +1,6 @@
{
"dependencies": {
"browser-ui-test": "^0.21.1",
"browser-ui-test": "^0.21.3",
"es-check": "^6.2.1",
"eslint": "^8.57.1",
"eslint-js": "github:eslint/js",

View file

@ -95,9 +95,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.23"
version = "1.2.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766"
checksum = "4ad45f4f74e4e20eaa392913b7b33a7091c87e59628f4dd27888205ad888843c"
dependencies = [
"shlex",
]

View file

@ -32,7 +32,7 @@ test = false
# Most of the time updating these dependencies requires modifications to the
# bootstrap codebase(e.g., https://github.com/rust-lang/rust/issues/124565);
# otherwise, some targets will fail. That's why these dependencies are explicitly pinned.
cc = "=1.2.23"
cc = "=1.2.28"
cmake = "=0.1.54"
build_helper = { path = "../build_helper" }

View file

@ -8,8 +8,8 @@ use crate::core::build_steps::compile::{
};
use crate::core::build_steps::tool;
use crate::core::build_steps::tool::{
COMPILETEST_ALLOW_FEATURES, SourceType, ToolTargetBuildMode, get_tool_target_compiler,
prepare_tool_cargo,
COMPILETEST_ALLOW_FEATURES, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, ToolTargetBuildMode,
get_tool_target_compiler, prepare_tool_cargo,
};
use crate::core::builder::{
self, Alias, Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
@ -791,7 +791,7 @@ tool_check_step!(MiroptTestTools {
tool_check_step!(TestFloatParse {
path: "src/tools/test-float-parse",
mode: |_builder| Mode::ToolStd,
allow_features: tool::TestFloatParse::ALLOW_FEATURES
allow_features: TEST_FLOAT_PARSE_ALLOW_FEATURES
});
tool_check_step!(FeaturesStatusDump {
path: "src/tools/features-status-dump",

View file

@ -791,7 +791,11 @@ fn doc_std(
}
/// Prepare a compiler that will be able to document something for `target` at `stage`.
fn prepare_doc_compiler(builder: &Builder<'_>, target: TargetSelection, stage: u32) -> Compiler {
pub fn prepare_doc_compiler(
builder: &Builder<'_>,
target: TargetSelection,
stage: u32,
) -> Compiler {
assert!(stage > 0, "Cannot document anything in stage 0");
let build_compiler = builder.compiler(stage - 1, builder.host_target);
builder.std(build_compiler, target);
@ -1289,6 +1293,8 @@ impl Step for RustcBook {
// functional sysroot.
builder.std(self.build_compiler, self.target);
let mut cmd = builder.tool_cmd(Tool::LintDocs);
cmd.arg("--build-rustc-stage");
cmd.arg(self.build_compiler.stage.to_string());
cmd.arg("--src");
cmd.arg(builder.src.join("compiler"));
cmd.arg("--out");

File diff suppressed because it is too large Load diff

View file

@ -1539,42 +1539,7 @@ tool_rustc_extended!(Rustfmt {
add_bins_to_sysroot: ["rustfmt"]
});
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TestFloatParse {
pub host: TargetSelection,
}
impl TestFloatParse {
pub const ALLOW_FEATURES: &'static str = "f16,cfg_target_has_reliable_f16_f128";
}
impl Step for TestFloatParse {
type Output = ToolBuildResult;
const IS_HOST: bool = true;
const DEFAULT: bool = false;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/test-float-parse")
}
fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
let bootstrap_host = builder.config.host_target;
let compiler = builder.compiler(builder.top_stage, bootstrap_host);
builder.ensure(ToolBuild {
build_compiler: compiler,
target: bootstrap_host,
tool: "test-float-parse",
mode: Mode::ToolStd,
path: "src/tools/test-float-parse",
source_type: SourceType::InTree,
extra_features: Vec::new(),
allow_features: Self::ALLOW_FEATURES,
cargo_args: Vec::new(),
artifact_kind: ToolArtifactKind::Binary,
})
}
}
pub const TEST_FLOAT_PARSE_ALLOW_FEATURES: &str = "f16,cfg_target_has_reliable_f16_f128";
impl Builder<'_> {
/// Gets a `BootstrapCommand` which is ready to run `tool` in `stage` built for

View file

@ -295,7 +295,7 @@ pub fn crate_description(crates: &[impl AsRef<str>]) -> String {
return "".into();
}
let mut descr = String::from(" {");
let mut descr = String::from("{");
descr.push_str(crates[0].as_ref());
for krate in &crates[1..] {
descr.push_str(", ");

View file

@ -2037,13 +2037,316 @@ mod snapshot {
.render_steps(), @"[check] rustc 0 <host> -> RunMakeSupport 1 <host>");
}
fn prepare_test_config(ctx: &TestCtx) -> ConfigBuilder {
ctx.config("test")
// Bootstrap only runs by default on CI, so we have to emulate that also locally.
.args(&["--ci", "true"])
// These rustdoc tests requires nodejs to be present.
// We can't easily opt out of it, so if it is present on the local PC, the test
// would have different result on CI, where nodejs might be missing.
.args(&["--skip", "rustdoc-js-std"])
.args(&["--skip", "rustdoc-js"])
.args(&["--skip", "rustdoc-gui"])
}
#[test]
fn test_all_stage_1() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
prepare_test_config(&ctx)
.render_steps(), @r"
[build] rustc 0 <host> -> Tidy 1 <host>
[test] tidy <>
[build] rustdoc 0 <host>
[build] llvm <host>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustc 0 <host> -> Compiletest 1 <host>
[test] compiletest-ui 1 <host>
[test] compiletest-crashes 1 <host>
[build] rustc 0 <host> -> CoverageDump 1 <host>
[test] compiletest-coverage 1 <host>
[test] compiletest-coverage 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[test] compiletest-mir-opt 1 <host>
[test] compiletest-codegen-llvm 1 <host>
[test] compiletest-codegen-units 1 <host>
[test] compiletest-assembly-llvm 1 <host>
[test] compiletest-incremental 1 <host>
[test] compiletest-debuginfo 1 <host>
[test] compiletest-ui-fulldeps 1 <host>
[build] rustdoc 1 <host>
[test] compiletest-rustdoc 1 <host>
[test] compiletest-coverage-run-rustdoc 1 <host>
[test] compiletest-pretty 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustc 0 <host> -> std 0 <host>
[test] rustc 0 <host> -> CrateLibrustc 1 <host>
[build] rustc 1 <host> -> rustc 2 <host>
[test] crate-bootstrap <host> src/tools/coverage-dump
[test] crate-bootstrap <host> src/tools/jsondoclint
[test] crate-bootstrap <host> src/tools/replace-version-placeholder
[test] crate-bootstrap <host> tidyselftest
[build] rustc 0 <host> -> UnstableBookGen 1 <host>
[build] rustc 0 <host> -> Rustbook 1 <host>
[doc] unstable-book (book) <host>
[doc] book (book) <host>
[doc] book/first-edition (book) <host>
[doc] book/second-edition (book) <host>
[doc] book/2018-edition (book) <host>
[doc] rustc 0 <host> -> standalone 1 <host>
[doc] rustc 1 <host> -> std 1 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
[build] rustc 0 <host> -> error-index 1 <host>
[doc] rustc 0 <host> -> error-index 1 <host>
[doc] nomicon (book) <host>
[doc] rustc 1 <host> -> reference (book) 2 <host>
[doc] rustdoc (book) <host>
[doc] rust-by-example (book) <host>
[build] rustc 0 <host> -> LintDocs 1 <host>
[doc] rustc (book) <host>
[doc] cargo (book) <host>
[doc] clippy (book) <host>
[doc] embedded-book (book) <host>
[doc] edition-guide (book) <host>
[doc] style-guide (book) <host>
[doc] rustc 0 <host> -> releases 1 <host>
[build] rustc 0 <host> -> Linkchecker 1 <host>
[test] link-check <host>
[test] tier-check <host>
[test] rustc 0 <host> -> rust-analyzer 1 <host>
[build] rustc 0 <host> -> RustdocTheme 1 <host>
[test] rustdoc-theme 1 <host>
[test] compiletest-rustdoc-ui 1 <host>
[build] rustc 0 <host> -> JsonDocCk 1 <host>
[build] rustc 0 <host> -> JsonDocLint 1 <host>
[test] compiletest-rustdoc-json 1 <host>
[doc] rustc 0 <host> -> rustc 1 <host>
[build] rustc 0 <host> -> HtmlChecker 1 <host>
[test] html-check <host>
[build] rustc 0 <host> -> RunMakeSupport 1 <host>
[build] rustc 0 <host> -> cargo 1 <host>
[test] compiletest-run-make 1 <host>
");
}
#[test]
fn test_compiletest_suites_stage1() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
ctx.config("test")
.args(&["ui", "ui-fulldeps", "run-make", "rustdoc", "rustdoc-gui", "incremental"])
.render_steps(), @r"
[build] llvm <host>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustc 0 <host> -> Compiletest 1 <host>
[test] compiletest-ui 1 <host>
[test] compiletest-ui-fulldeps 1 <host>
[build] rustc 0 <host> -> RunMakeSupport 1 <host>
[build] rustc 0 <host> -> cargo 1 <host>
[build] rustdoc 1 <host>
[test] compiletest-run-make 1 <host>
[test] compiletest-rustdoc 1 <host>
[build] rustc 0 <host> -> RustdocGUITest 1 <host>
[test] rustdoc-gui 1 <host>
[test] compiletest-incremental 1 <host>
[build] rustc 1 <host> -> rustc 2 <host>
");
}
#[test]
fn test_compiletest_suites_stage2() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
ctx.config("test")
.args(&["ui", "ui-fulldeps", "run-make", "rustdoc", "rustdoc-gui", "incremental"])
.stage(2)
.render_steps(), @r"
[build] llvm <host>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustc 1 <host> -> rustc 2 <host>
[build] rustc 2 <host> -> std 2 <host>
[build] rustc 0 <host> -> Compiletest 1 <host>
[test] compiletest-ui 2 <host>
[build] rustc 2 <host> -> rustc 3 <host>
[test] compiletest-ui-fulldeps 2 <host>
[build] rustc 0 <host> -> RunMakeSupport 1 <host>
[build] rustc 1 <host> -> cargo 2 <host>
[build] rustdoc 2 <host>
[test] compiletest-run-make 2 <host>
[test] compiletest-rustdoc 2 <host>
[build] rustc 0 <host> -> RustdocGUITest 1 <host>
[test] rustdoc-gui 2 <host>
[test] compiletest-incremental 2 <host>
[build] rustdoc 1 <host>
");
}
#[test]
fn test_compiletest_suites_stage2_cross() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
ctx.config("test")
.hosts(&[TEST_TRIPLE_1])
.targets(&[TEST_TRIPLE_1])
.args(&["ui", "ui-fulldeps", "run-make", "rustdoc", "rustdoc-gui", "incremental"])
.stage(2)
.render_steps(), @r"
[build] llvm <host>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustc 1 <host> -> rustc 2 <host>
[build] rustc 2 <host> -> std 2 <host>
[build] rustc 0 <host> -> Compiletest 1 <host>
[build] rustc 1 <host> -> std 1 <target1>
[build] rustc 2 <host> -> std 2 <target1>
[test] compiletest-ui 2 <target1>
[build] llvm <target1>
[build] rustc 2 <host> -> rustc 3 <target1>
[test] compiletest-ui-fulldeps 2 <target1>
[build] rustc 0 <host> -> RunMakeSupport 1 <host>
[build] rustc 1 <host> -> cargo 2 <host>
[build] rustdoc 2 <host>
[test] compiletest-run-make 2 <target1>
[test] compiletest-rustdoc 2 <target1>
[build] rustc 0 <host> -> RustdocGUITest 1 <host>
[test] rustdoc-gui 2 <target1>
[test] compiletest-incremental 2 <target1>
[build] rustc 1 <host> -> rustc 2 <target1>
[build] rustdoc 1 <host>
[build] rustc 2 <target1> -> std 2 <target1>
[build] rustdoc 2 <target1>
");
}
#[test]
fn test_all_stage_2() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
prepare_test_config(&ctx)
.stage(2)
.render_steps(), @r"
[build] rustc 0 <host> -> Tidy 1 <host>
[test] tidy <>
[build] rustdoc 0 <host>
[build] llvm <host>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustc 1 <host> -> rustc 2 <host>
[build] rustc 2 <host> -> std 2 <host>
[build] rustc 0 <host> -> Compiletest 1 <host>
[test] compiletest-ui 2 <host>
[test] compiletest-crashes 2 <host>
[build] rustc 0 <host> -> CoverageDump 1 <host>
[test] compiletest-coverage 2 <host>
[test] compiletest-coverage 2 <host>
[build] rustc 2 <host> -> std 2 <host>
[test] compiletest-mir-opt 2 <host>
[test] compiletest-codegen-llvm 2 <host>
[test] compiletest-codegen-units 2 <host>
[test] compiletest-assembly-llvm 2 <host>
[test] compiletest-incremental 2 <host>
[test] compiletest-debuginfo 2 <host>
[build] rustc 2 <host> -> rustc 3 <host>
[test] compiletest-ui-fulldeps 2 <host>
[build] rustdoc 2 <host>
[test] compiletest-rustdoc 2 <host>
[test] compiletest-coverage-run-rustdoc 2 <host>
[test] compiletest-pretty 2 <host>
[build] rustc 2 <host> -> std 2 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustdoc 1 <host>
[test] rustc 1 <host> -> CrateLibrustc 2 <host>
[test] crate-bootstrap <host> src/tools/coverage-dump
[test] crate-bootstrap <host> src/tools/jsondoclint
[test] crate-bootstrap <host> src/tools/replace-version-placeholder
[test] crate-bootstrap <host> tidyselftest
[build] rustc 0 <host> -> UnstableBookGen 1 <host>
[build] rustc 0 <host> -> Rustbook 1 <host>
[doc] unstable-book (book) <host>
[doc] book (book) <host>
[doc] book/first-edition (book) <host>
[doc] book/second-edition (book) <host>
[doc] book/2018-edition (book) <host>
[doc] rustc 1 <host> -> standalone 2 <host>
[doc] rustc 1 <host> -> std 1 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
[build] rustc 1 <host> -> error-index 2 <host>
[doc] rustc 1 <host> -> error-index 2 <host>
[doc] nomicon (book) <host>
[doc] rustc 1 <host> -> reference (book) 2 <host>
[doc] rustdoc (book) <host>
[doc] rust-by-example (book) <host>
[build] rustc 0 <host> -> LintDocs 1 <host>
[doc] rustc (book) <host>
[doc] cargo (book) <host>
[doc] clippy (book) <host>
[doc] embedded-book (book) <host>
[doc] edition-guide (book) <host>
[doc] style-guide (book) <host>
[doc] rustc 1 <host> -> releases 2 <host>
[build] rustc 0 <host> -> Linkchecker 1 <host>
[test] link-check <host>
[test] tier-check <host>
[test] rustc 1 <host> -> rust-analyzer 2 <host>
[doc] rustc (book) <host>
[test] rustc 1 <host> -> lint-docs 2 <host>
[build] rustc 0 <host> -> RustdocTheme 1 <host>
[test] rustdoc-theme 2 <host>
[test] compiletest-rustdoc-ui 2 <host>
[build] rustc 0 <host> -> JsonDocCk 1 <host>
[build] rustc 0 <host> -> JsonDocLint 1 <host>
[test] compiletest-rustdoc-json 2 <host>
[doc] rustc 1 <host> -> rustc 2 <host>
[build] rustc 0 <host> -> HtmlChecker 1 <host>
[test] html-check <host>
[build] rustc 0 <host> -> RunMakeSupport 1 <host>
[build] rustc 1 <host> -> cargo 2 <host>
[test] compiletest-run-make 2 <host>
");
}
#[test]
fn test_compiler_stage_1() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
ctx.config("test")
.path("compiler")
.stage(1)
.render_steps(), @r"
[build] llvm <host>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 0 <host> -> std 0 <host>
[build] rustdoc 0 <host>
[test] rustc 0 <host> -> CrateLibrustc 1 <host>
");
}
#[test]
fn test_compiler_stage_2() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
ctx.config("test")
.path("compiler")
.stage(2)
.render_steps(), @r"
[build] llvm <host>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustc 1 <host> -> rustc 2 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustdoc 1 <host>
[test] rustc 1 <host> -> CrateLibrustc 2 <host>
");
}
#[test]
fn test_exclude() {
let ctx = TestCtx::new();
let steps = ctx.config("test").args(&["--skip", "src/tools/tidy"]).get_steps();
let host = TargetSelection::from_user(&host_target());
steps.assert_contains(StepMetadata::test("RustdocUi", host));
steps.assert_contains(StepMetadata::test("compiletest-rustdoc-ui", host).stage(1));
steps.assert_not_contains(test::Tidy);
}
@ -2054,13 +2357,15 @@ mod snapshot {
let get_steps = |args: &[&str]| ctx.config("test").args(args).get_steps();
let rustc_metadata =
|| StepMetadata::test("CrateLibrustc", host).built_by(Compiler::new(0, host));
// Ensure our test is valid, and `test::Rustc` would be run without the exclude.
get_steps(&[]).assert_contains(StepMetadata::test("CrateLibrustc", host));
get_steps(&[]).assert_contains(rustc_metadata());
let steps = get_steps(&["--skip", "compiler/rustc_data_structures"]);
// Ensure tests for rustc are not skipped.
steps.assert_contains(StepMetadata::test("CrateLibrustc", host));
steps.assert_contains(rustc_metadata());
steps.assert_contains_fuzzy(StepMetadata::build("rustc", host));
}
@ -2077,6 +2382,7 @@ mod snapshot {
[build] rustc 1 <host> -> std 1 <host>
[build] rustdoc 1 <host>
[build] rustdoc 0 <host>
[test] rustc 0 <host> -> cargo 1 <host>
");
}
@ -2096,6 +2402,7 @@ mod snapshot {
[build] rustc 2 <host> -> std 2 <host>
[build] rustdoc 2 <host>
[build] rustdoc 1 <host>
[test] rustc 1 <host> -> cargo 2 <host>
");
}
@ -2116,6 +2423,19 @@ mod snapshot {
");
}
#[test]
fn test_tier_check() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
ctx.config("test")
.path("tier-check")
.render_steps(), @r"
[build] llvm <host>
[build] rustc 0 <host> -> rustc 1 <host>
[test] tier-check <host>
");
}
#[test]
fn doc_all() {
let ctx = TestCtx::new();

View file

@ -1002,13 +1002,17 @@ impl Config {
(0, Subcommand::Install) => {
check_stage0("install");
}
(0, Subcommand::Test { .. }) if build_compiletest_allow_stage0 != Some(true) => {
eprintln!(
"ERROR: cannot test anything on stage 0. Use at least stage 1. If you want to run compiletest with an external stage0 toolchain, enable `build.compiletest-allow-stage0`."
);
exit!(1);
}
_ => {}
}
if flags_compile_time_deps && !matches!(flags_cmd, Subcommand::Check { .. }) {
eprintln!(
"WARNING: Can't use --compile-time-deps with any subcommand other than check."
);
eprintln!("ERROR: Can't use --compile-time-deps with any subcommand other than check.");
exit!(1);
}

View file

@ -426,22 +426,22 @@ forward! {
download_rustc() -> bool,
}
/// A mostly temporary helper struct before we can migrate everything in bootstrap to use
/// the concept of a build compiler.
struct HostAndStage {
host: TargetSelection,
/// An alternative way of specifying what target and stage is involved in some bootstrap activity.
/// Ideally using a `Compiler` directly should be preferred.
struct TargetAndStage {
target: TargetSelection,
stage: u32,
}
impl From<(TargetSelection, u32)> for HostAndStage {
fn from((host, stage): (TargetSelection, u32)) -> Self {
Self { host, stage }
impl From<(TargetSelection, u32)> for TargetAndStage {
fn from((target, stage): (TargetSelection, u32)) -> Self {
Self { target, stage }
}
}
impl From<Compiler> for HostAndStage {
impl From<Compiler> for TargetAndStage {
fn from(compiler: Compiler) -> Self {
Self { host: compiler.host, stage: compiler.stage }
Self { target: compiler.host, stage: compiler.stage }
}
}
@ -1109,11 +1109,12 @@ impl Build {
/// Return a `Group` guard for a [`Step`] that:
/// - Performs `action`
/// - If the action is `Kind::Test`, use [`Build::msg_test`] instead.
/// - On `what`
/// - Where `what` possibly corresponds to a `mode`
/// - `action` is performed using the given build compiler (`host_and_stage`).
/// - Since some steps do not use the concept of a build compiler yet, it is also possible
/// to pass the host and stage explicitly.
/// - `action` is performed with/on the given compiler (`target_and_stage`).
/// - Since for some steps it is not possible to pass a single compiler here, it is also
/// possible to pass the host and stage explicitly.
/// - With a given `target`.
///
/// [`Step`]: crate::core::builder::Step
@ -1124,13 +1125,19 @@ impl Build {
action: impl Into<Kind>,
what: impl Display,
mode: impl Into<Option<Mode>>,
host_and_stage: impl Into<HostAndStage>,
target_and_stage: impl Into<TargetAndStage>,
target: impl Into<Option<TargetSelection>>,
) -> Option<gha::Group> {
let host_and_stage = host_and_stage.into();
let target_and_stage = target_and_stage.into();
let action = action.into();
assert!(
action != Kind::Test,
"Please use `Build::msg_test` instead of `Build::msg(Kind::Test)`"
);
let actual_stage = match mode.into() {
// Std has the same stage as the compiler that builds it
Some(Mode::Std) => host_and_stage.stage,
Some(Mode::Std) => target_and_stage.stage,
// Other things have stage corresponding to their build compiler + 1
Some(
Mode::Rustc
@ -1140,18 +1147,22 @@ impl Build {
| Mode::ToolStd
| Mode::ToolRustc,
)
| None => host_and_stage.stage + 1,
| None => target_and_stage.stage + 1,
};
let action = action.into().description();
let msg = |fmt| format!("{action} stage{actual_stage} {what}{fmt}");
let action = action.description();
let what = what.to_string();
let msg = |fmt| {
let space = if !what.is_empty() { " " } else { "" };
format!("{action} stage{actual_stage} {what}{space}{fmt}")
};
let msg = if let Some(target) = target.into() {
let build_stage = host_and_stage.stage;
let host = host_and_stage.host;
let build_stage = target_and_stage.stage;
let host = target_and_stage.target;
if host == target {
msg(format_args!(" (stage{build_stage} -> stage{actual_stage}, {target})"))
msg(format_args!("(stage{build_stage} -> stage{actual_stage}, {target})"))
} else {
msg(format_args!(" (stage{build_stage}:{host} -> stage{actual_stage}:{target})"))
msg(format_args!("(stage{build_stage}:{host} -> stage{actual_stage}:{target})"))
}
} else {
msg(format_args!(""))
@ -1159,6 +1170,24 @@ impl Build {
self.group(&msg)
}
/// Return a `Group` guard for a [`Step`] that tests `what` with the given `stage` and `target`.
/// Use this instead of [`Build::msg`] for test steps, because for them it is not always clear
/// what exactly is a build compiler.
///
/// [`Step`]: crate::core::builder::Step
#[must_use = "Groups should not be dropped until the Step finishes running"]
#[track_caller]
fn msg_test(
&self,
what: impl Display,
target: TargetSelection,
stage: u32,
) -> Option<gha::Group> {
let action = Kind::Test.description();
let msg = format!("{action} stage{stage} {what} ({target})");
self.group(&msg)
}
/// Return a `Group` guard for a [`Step`] that is only built once and isn't affected by `--stage`.
///
/// [`Step`]: crate::core::builder::Step
@ -2086,11 +2115,6 @@ impl Compiler {
self.forced_compiler = forced_compiler;
}
pub fn with_stage(mut self, stage: u32) -> Compiler {
self.stage = stage;
self
}
/// Returns `true` if this is a snapshot compiler for `build`'s configuration
pub fn is_snapshot(&self, build: &Build) -> bool {
self.stage == 0 && self.host == build.host_target

View file

@ -531,4 +531,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
severity: ChangeSeverity::Info,
summary: "It is now possible to `check/build/dist` the standard stage 0 library if you use a stage0 rustc built from in-tree sources. This is useful for quickly cross-compiling the standard library. You have to enable build.local-rebuild for this to work.",
},
ChangeInfo {
change_id: 145663,
severity: ChangeSeverity::Warning,
summary: "It is no longer possible to `x test` with stage 0, except for running compiletest and opting into `build.compiletest-allow-stage0`.",
},
];

View file

@ -168,7 +168,11 @@ mod inner {
impl TracingGuard {
pub fn copy_to_dir(self, dir: &std::path::Path) {
drop(self.guard);
std::fs::rename(&self.chrome_tracing_path, dir.join("chrome-trace.json")).unwrap();
crate::utils::helpers::move_file(
&self.chrome_tracing_path,
dir.join("chrome-trace.json"),
)
.unwrap();
}
}

View file

@ -44,5 +44,5 @@ RUN bash -c 'npm install -g eslint@$(cat /tmp/eslint.version)'
# NOTE: intentionally uses python2 for x.py so we can test it still works.
# validate-toolstate only runs in our CI, so it's ok for it to only support python3.
ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 \
ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test \
src/tools/tidy tidyselftest --extra-checks=py,cpp,js,spellcheck

View file

@ -2,7 +2,8 @@
set -euo pipefail
LINUX_VERSION=v6.16-rc1
# https://github.com/rust-lang/rust/pull/144443
LINUX_VERSION=7770d51bce622b13195b2d3c85407282fc9c27e5
# Build rustc, rustdoc, cargo, clippy-driver and rustfmt
../x.py build --stage 2 library rustdoc clippy rustfmt

View file

@ -116,6 +116,7 @@
- [\*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md)
- [\*-unknown-hermit](platform-support/hermit.md)
- [\*-unknown-freebsd](platform-support/freebsd.md)
- [\*-unknown-managarm-mlibc](platform-support/managarm.md)
- [\*-unknown-netbsd\*](platform-support/netbsd.md)
- [\*-unknown-openbsd](platform-support/openbsd.md)
- [\*-unknown-redox](platform-support/redox.md)

View file

@ -258,6 +258,7 @@ target | std | host | notes
[`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit
[`aarch64-unknown-illumos`](platform-support/illumos.md) | ✓ | ✓ | ARM64 illumos
`aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI)
[`aarch64-unknown-managarm-mlibc`](platform-support/managarm.md) | ? | | ARM64 Managarm
[`aarch64-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD
[`aarch64-unknown-nto-qnx700`](platform-support/nto-qnx.md) | ? | | ARM64 QNX Neutrino 7.0 RTOS |
[`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS with default network stack (io-pkt) |
@ -388,6 +389,7 @@ target | std | host | notes
`riscv64gc-unknown-freebsd` | ? | | RISC-V FreeBSD
`riscv64gc-unknown-fuchsia` | ? | | RISC-V Fuchsia
[`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ | | RISC-V Hermit
[`riscv64gc-unknown-managarm-mlibc`](platform-support/managarm.md) | ? | | RISC-V Managarm
[`riscv64gc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | RISC-V NetBSD
[`riscv64gc-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 64bit with NuttX
[`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64
@ -428,6 +430,7 @@ target | std | host | notes
[`x86_64-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 64-bit GNU/Hurd
`x86_64-unknown-l4re-uclibc` | ? | |
[`x86_64-unknown-linux-none`](platform-support/x86_64-unknown-linux-none.md) | * | | 64-bit Linux with no libc
[`x86_64-unknown-managarm-mlibc`](platform-support/managarm.md) | ? | | x86_64 Managarm
[`x86_64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 64-bit OpenBSD
[`x86_64-unknown-trusty`](platform-support/trusty.md) | ✓ | |
`x86_64-uwp-windows-gnu` | ✓ | |

View file

@ -0,0 +1,53 @@
# `*-unknown-managarm-mlibc`
**Tier: 3**
## Target Maintainers
- [@no92](https://github.com/no92)
- [@64](https://github.com/64)
- [@Dennisbonke](https://github.com/Dennisbonke)
## Requirements
This target is cross-compiled. There is currently no support for `std` yet. It generates binaries in the ELF format. Currently, we support the `x86_64`, `aarch64` and `riscv64gc` architectures. The examples below `$ARCH` should be substituted for one of the supported architectures.
## Building the target
Managarm has upstream support in LLVM since the release of 21.1.0.
Set up your `bootstrap.toml` like this:
```toml
change-id = 142379
[llvm]
targets = "X86;AArch64;RISCV"
download-ci-llvm = false
[build]
target = ["$ARCH-unknown-managarm-mlibc", "x86_64-unknown-linux-gnu"]
[target.x86_64-unknown-linux-gnu]
llvm-config = "/path/to/your/llvm/bin/llvm-config"
[target.$ARCH-unknown-managarm-mlibc]
llvm-config = "/path/to/your/llvm/bin/llvm-config"
```
## Building Rust programs
Build a `$ARCH-managarm-gcc` using our [gcc fork](https://github.com/managarm/gcc).
```toml
[build]
rustc = "/path/to/the/rust-prefix/bin/rustc"
target = "$ARCH-unknown-managarm-mlibc"
[target.$ARCH-unknown-managarm-mlibc]
linker = "/path/to/the/managarm-gcc/bin/$ARCH-managarm-gcc"
```
## Testing
This target does not support running the Rust testsuite yet.

View file

@ -289,8 +289,26 @@ impl SerializedSearchIndex {
(Some(self_type_data), None) => Some(self_type_data),
(None, Some(other_type_data)) => Some(TypeData {
search_unbox: other_type_data.search_unbox,
inverted_function_signature_index: other_type_data
.inverted_function_signature_index
inverted_function_inputs_index: other_type_data
.inverted_function_inputs_index
.iter()
.cloned()
.map(|mut list: Vec<u32>| {
for fnid in &mut list {
assert!(
other.function_data
[usize::try_from(*fnid).unwrap()]
.is_some(),
);
// this is valid because we call `self.push()` once, exactly, for every entry,
// even if we're just pushing a tombstone
*fnid += u32::try_from(other_entryid_offset).unwrap();
}
list
})
.collect(),
inverted_function_output_index: other_type_data
.inverted_function_output_index
.iter()
.cloned()
.map(|mut list: Vec<u32>| {
@ -310,18 +328,42 @@ impl SerializedSearchIndex {
}),
(Some(mut self_type_data), Some(other_type_data)) => {
for (size, other_list) in other_type_data
.inverted_function_signature_index
.inverted_function_inputs_index
.iter()
.enumerate()
{
while self_type_data.inverted_function_signature_index.len()
while self_type_data.inverted_function_inputs_index.len()
<= size
{
self_type_data
.inverted_function_signature_index
.inverted_function_inputs_index
.push(Vec::new());
}
self_type_data.inverted_function_signature_index[size].extend(
self_type_data.inverted_function_inputs_index[size].extend(
other_list.iter().copied().map(|fnid| {
assert!(
other.function_data[usize::try_from(fnid).unwrap()]
.is_some(),
);
// this is valid because we call `self.push()` once, exactly, for every entry,
// even if we're just pushing a tombstone
fnid + u32::try_from(other_entryid_offset).unwrap()
}),
)
}
for (size, other_list) in other_type_data
.inverted_function_output_index
.iter()
.enumerate()
{
while self_type_data.inverted_function_output_index.len()
<= size
{
self_type_data
.inverted_function_output_index
.push(Vec::new());
}
self_type_data.inverted_function_output_index[size].extend(
other_list.iter().copied().map(|fnid| {
assert!(
other.function_data[usize::try_from(fnid).unwrap()]
@ -443,8 +485,25 @@ impl SerializedSearchIndex {
param_names: function_data.param_names.clone(),
}),
other.type_data[other_entryid].as_ref().map(|type_data| TypeData {
inverted_function_signature_index: type_data
.inverted_function_signature_index
inverted_function_inputs_index: type_data
.inverted_function_inputs_index
.iter()
.cloned()
.map(|mut list| {
for fnid in &mut list {
assert!(
other.function_data[usize::try_from(*fnid).unwrap()]
.is_some(),
);
// this is valid because we call `self.push()` once, exactly, for every entry,
// even if we're just pushing a tombstone
*fnid += u32::try_from(other_entryid_offset).unwrap();
}
list
})
.collect(),
inverted_function_output_index: type_data
.inverted_function_output_index
.iter()
.cloned()
.map(|mut list| {
@ -599,9 +658,13 @@ impl SerializedSearchIndex {
},
),
self.type_data[id].as_ref().map(
|TypeData { search_unbox, inverted_function_signature_index }| {
let inverted_function_signature_index: Vec<Vec<u32>> =
inverted_function_signature_index
|TypeData {
search_unbox,
inverted_function_inputs_index,
inverted_function_output_index,
}| {
let inverted_function_inputs_index: Vec<Vec<u32>> =
inverted_function_inputs_index
.iter()
.cloned()
.map(|mut list| {
@ -615,7 +678,26 @@ impl SerializedSearchIndex {
list
})
.collect();
TypeData { search_unbox: *search_unbox, inverted_function_signature_index }
let inverted_function_output_index: Vec<Vec<u32>> =
inverted_function_output_index
.iter()
.cloned()
.map(|mut list| {
for id in &mut list {
*id = u32::try_from(
*map.get(&usize::try_from(*id).unwrap()).unwrap(),
)
.unwrap();
}
list.sort();
list
})
.collect();
TypeData {
search_unbox: *search_unbox,
inverted_function_inputs_index,
inverted_function_output_index,
}
},
),
self.alias_pointers[id].and_then(|alias| map.get(&alias).copied()),
@ -934,18 +1016,20 @@ struct TypeData {
/// | `Unboxable<Inner>` | yes | no | no |
/// | `Inner<Unboxable>` | no | no | yes |
search_unbox: bool,
/// List of functions that mention this type in their type signature.
/// List of functions that mention this type in their type signature,
/// on the left side of the `->` arrow.
///
/// - The outermost list has one entry per alpha-normalized generic.
///
/// - The second layer is sorted by number of types that appear in the
/// - The outer layer is sorted by number of types that appear in the
/// type signature. The search engine iterates over these in order from
/// smallest to largest. Functions with less stuff in their type
/// signature are more likely to be what the user wants, because we never
/// show functions that are *missing* parts of the query, so removing..
///
/// - The final layer is the list of functions.
inverted_function_signature_index: Vec<Vec<u32>>,
/// - The inner layer is the list of functions.
inverted_function_inputs_index: Vec<Vec<u32>>,
/// List of functions that mention this type in their type signature,
/// on the right side of the `->` arrow.
inverted_function_output_index: Vec<Vec<u32>>,
}
impl Serialize for TypeData {
@ -953,15 +1037,21 @@ impl Serialize for TypeData {
where
S: Serializer,
{
if self.search_unbox || !self.inverted_function_signature_index.is_empty() {
if self.search_unbox
|| !self.inverted_function_inputs_index.is_empty()
|| !self.inverted_function_output_index.is_empty()
{
let mut seq = serializer.serialize_seq(None)?;
if !self.inverted_function_signature_index.is_empty() {
let mut buf = Vec::new();
encode::write_postings_to_string(&self.inverted_function_signature_index, &mut buf);
let mut serialized_result = Vec::new();
stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result);
seq.serialize_element(&String::from_utf8(serialized_result).unwrap())?;
}
let mut buf = Vec::new();
encode::write_postings_to_string(&self.inverted_function_inputs_index, &mut buf);
let mut serialized_result = Vec::new();
stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result);
seq.serialize_element(&str::from_utf8(&serialized_result).unwrap())?;
buf.clear();
serialized_result.clear();
encode::write_postings_to_string(&self.inverted_function_output_index, &mut buf);
stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result);
seq.serialize_element(&str::from_utf8(&serialized_result).unwrap())?;
if self.search_unbox {
seq.serialize_element(&1)?;
}
@ -984,21 +1074,39 @@ impl<'de> Deserialize<'de> for TypeData {
write!(formatter, "type data")
}
fn visit_none<E>(self) -> Result<TypeData, E> {
Ok(TypeData { inverted_function_signature_index: vec![], search_unbox: false })
Ok(TypeData {
inverted_function_inputs_index: vec![],
inverted_function_output_index: vec![],
search_unbox: false,
})
}
fn visit_seq<A: de::SeqAccess<'de>>(self, mut v: A) -> Result<TypeData, A::Error> {
let inverted_function_signature_index: String =
let inverted_function_inputs_index: String =
v.next_element()?.unwrap_or(String::new());
let inverted_function_output_index: String =
v.next_element()?.unwrap_or(String::new());
let search_unbox: u32 = v.next_element()?.unwrap_or(0);
let mut idx: Vec<u8> = Vec::new();
stringdex_internals::decode::read_base64_from_bytes(
inverted_function_signature_index.as_bytes(),
inverted_function_inputs_index.as_bytes(),
&mut idx,
)
.unwrap();
let mut inverted_function_signature_index = Vec::new();
encode::read_postings_from_string(&mut inverted_function_signature_index, &idx);
Ok(TypeData { inverted_function_signature_index, search_unbox: search_unbox == 1 })
let mut inverted_function_inputs_index = Vec::new();
encode::read_postings_from_string(&mut inverted_function_inputs_index, &idx);
idx.clear();
stringdex_internals::decode::read_base64_from_bytes(
inverted_function_output_index.as_bytes(),
&mut idx,
)
.unwrap();
let mut inverted_function_output_index = Vec::new();
encode::read_postings_from_string(&mut inverted_function_output_index, &idx);
Ok(TypeData {
inverted_function_inputs_index,
inverted_function_output_index,
search_unbox: search_unbox == 1,
})
}
}
deserializer.deserialize_any(TypeDataVisitor)
@ -1222,8 +1330,16 @@ pub(crate) fn build_index(
let index = *index.get();
serialized_index.descs[index] = crate_doc;
for type_data in serialized_index.type_data.iter_mut() {
if let Some(TypeData { inverted_function_signature_index, .. }) = type_data {
for list in &mut inverted_function_signature_index[..] {
if let Some(TypeData {
inverted_function_inputs_index,
inverted_function_output_index,
..
}) = type_data
{
for list in inverted_function_inputs_index
.iter_mut()
.chain(inverted_function_output_index.iter_mut())
{
list.retain(|fnid| {
serialized_index.entry_data[usize::try_from(*fnid).unwrap()]
.as_ref()
@ -1449,7 +1565,8 @@ pub(crate) fn build_index(
if serialized_index.type_data[id].as_mut().is_none() {
serialized_index.type_data[id] = Some(TypeData {
search_unbox,
inverted_function_signature_index: Vec::new(),
inverted_function_inputs_index: Vec::new(),
inverted_function_output_index: Vec::new(),
});
} else if search_unbox {
serialized_index.type_data[id].as_mut().unwrap().search_unbox = true;
@ -1473,7 +1590,11 @@ pub(crate) fn build_index(
None
},
},
TypeData { search_unbox, inverted_function_signature_index: Vec::new() },
TypeData {
inverted_function_inputs_index: Vec::new(),
inverted_function_output_index: Vec::new(),
search_unbox,
},
);
pathid
}
@ -1695,13 +1816,14 @@ pub(crate) fn build_index(
}
}
if let Some(search_type) = &mut item.search_type {
let mut used_in_function_signature = BTreeSet::new();
let mut used_in_function_inputs = BTreeSet::new();
let mut used_in_function_output = BTreeSet::new();
for item in &mut search_type.inputs {
convert_render_type(
item,
cache,
&mut serialized_index,
&mut used_in_function_signature,
&mut used_in_function_inputs,
tcx,
);
}
@ -1710,20 +1832,44 @@ pub(crate) fn build_index(
item,
cache,
&mut serialized_index,
&mut used_in_function_signature,
&mut used_in_function_output,
tcx,
);
}
let mut used_in_constraints = Vec::new();
for constraint in &mut search_type.where_clause {
let mut used_in_constraint = BTreeSet::new();
for trait_ in &mut constraint[..] {
convert_render_type(
trait_,
cache,
&mut serialized_index,
&mut used_in_function_signature,
&mut used_in_constraint,
tcx,
);
}
used_in_constraints.push(used_in_constraint);
}
loop {
let mut inserted_any = false;
for (i, used_in_constraint) in used_in_constraints.iter().enumerate() {
let id = !(i as isize);
if used_in_function_inputs.contains(&id)
&& !used_in_function_inputs.is_superset(&used_in_constraint)
{
used_in_function_inputs.extend(used_in_constraint.iter().copied());
inserted_any = true;
}
if used_in_function_output.contains(&id)
&& !used_in_function_output.is_superset(&used_in_constraint)
{
used_in_function_output.extend(used_in_constraint.iter().copied());
inserted_any = true;
}
}
if !inserted_any {
break;
}
}
let search_type_size = search_type.size() +
// Artificially give struct fields a size of 8 instead of their real
@ -1746,13 +1892,13 @@ pub(crate) fn build_index(
.map(|sym| sym.map(|sym| sym.to_string()).unwrap_or(String::new()))
.collect::<Vec<String>>(),
});
for index in used_in_function_signature {
for index in used_in_function_inputs {
let postings = if index >= 0 {
assert!(serialized_index.path_data[index as usize].is_some());
&mut serialized_index.type_data[index as usize]
.as_mut()
.unwrap()
.inverted_function_signature_index
.inverted_function_inputs_index
} else {
let generic_id = usize::try_from(-index).unwrap() - 1;
for _ in serialized_index.generic_inverted_index.len()..=generic_id {
@ -1763,7 +1909,30 @@ pub(crate) fn build_index(
while postings.len() <= search_type_size {
postings.push(Vec::new());
}
postings[search_type_size].push(new_entry_id as u32);
if postings[search_type_size].last() != Some(&(new_entry_id as u32)) {
postings[search_type_size].push(new_entry_id as u32);
}
}
for index in used_in_function_output {
let postings = if index >= 0 {
assert!(serialized_index.path_data[index as usize].is_some());
&mut serialized_index.type_data[index as usize]
.as_mut()
.unwrap()
.inverted_function_output_index
} else {
let generic_id = usize::try_from(-index).unwrap() - 1;
for _ in serialized_index.generic_inverted_index.len()..=generic_id {
serialized_index.generic_inverted_index.push(Vec::new());
}
&mut serialized_index.generic_inverted_index[generic_id]
};
while postings.len() <= search_type_size {
postings.push(Vec::new());
}
if postings[search_type_size].last() != Some(&(new_entry_id as u32)) {
postings[search_type_size].push(new_entry_id as u32);
}
}
}
}

View file

@ -270,9 +270,12 @@ declare namespace rustdoc {
*/
interface TypeData {
searchUnbox: boolean,
invertedFunctionSignatureIndex: RoaringBitmap[],
invertedFunctionInputsIndex: RoaringBitmap[],
invertedFunctionOutputIndex: RoaringBitmap[],
}
type TypeInvertedIndexPolarity = "invertedFunctionInputsIndex" | "invertedFunctionOutputIndex";
/**
* A search entry of some sort.
*/

View file

@ -1696,7 +1696,7 @@ class DocSearch {
}
/**
* function_signature, param_names
* @type {[string, number] | [number] | [string] | [] | null}
* @type {[string, string, number] | [string, string] | [] | null}
*/
const raw = JSON.parse(encoded);
@ -1705,32 +1705,46 @@ class DocSearch {
}
let searchUnbox = false;
const invertedFunctionSignatureIndex = [];
const invertedFunctionInputsIndex = [];
const invertedFunctionOutputIndex = [];
if (typeof raw[0] === "string") {
if (raw[1]) {
if (raw[2]) {
searchUnbox = true;
}
// the inverted function signature index is a list of bitmaps,
// by number of types that appear in the function
let i = 0;
const pb = makeUint8ArrayFromBase64(raw[0]);
const l = pb.length;
let pb = makeUint8ArrayFromBase64(raw[0]);
let l = pb.length;
while (i < l) {
if (pb[i] === 0) {
invertedFunctionSignatureIndex.push(RoaringBitmap.empty());
invertedFunctionInputsIndex.push(RoaringBitmap.empty());
i += 1;
} else {
const bitmap = new RoaringBitmap(pb, i);
i += bitmap.consumed_len_bytes;
invertedFunctionSignatureIndex.push(bitmap);
invertedFunctionInputsIndex.push(bitmap);
}
}
i = 0;
pb = makeUint8ArrayFromBase64(raw[1]);
l = pb.length;
while (i < l) {
if (pb[i] === 0) {
invertedFunctionOutputIndex.push(RoaringBitmap.empty());
i += 1;
} else {
const bitmap = new RoaringBitmap(pb, i);
i += bitmap.consumed_len_bytes;
invertedFunctionOutputIndex.push(bitmap);
}
}
} else if (raw[0]) {
searchUnbox = true;
}
return { searchUnbox, invertedFunctionSignatureIndex };
return { searchUnbox, invertedFunctionInputsIndex, invertedFunctionOutputIndex };
}
/**
@ -4009,14 +4023,19 @@ class DocSearch {
* or anything else. This function returns all possible permutations.
*
* @param {rustdoc.ParserQueryElement|null} elem
* @param {rustdoc.TypeInvertedIndexPolarity} polarity
* @returns {Promise<PostingsList<rustdoc.QueryElement>[]>}
*/
const unpackPostingsList = async elem => {
const unpackPostingsList = async(elem, polarity) => {
if (!elem) {
return empty_postings_list;
}
const typeFilter = itemTypeFromName(elem.typeFilter);
const searchResults = await index.search(elem.normalizedPathLast);
const [searchResults, upla, uplb] = await Promise.all([
index.search(elem.normalizedPathLast),
unpackPostingsListAll(elem.generics, polarity),
unpackPostingsListBindings(elem.bindings, polarity),
]);
/**
* @type {Promise<[
* number,
@ -4039,7 +4058,7 @@ class DocSearch {
const types = (await Promise.all(typePromises))
.filter(([_id, name, ty, path]) =>
name !== null && name.toLowerCase() === elem.pathLast &&
ty && !ty.invertedFunctionSignatureIndex.every(bitmap => {
ty && !ty[polarity].every(bitmap => {
return bitmap.isEmpty();
}) &&
path && path.ty !== TY_ASSOCTYPE &&
@ -4078,7 +4097,7 @@ class DocSearch {
this.getPathData(id),
]);
if (name !== null && ty !== null && path !== null &&
!ty.invertedFunctionSignatureIndex.every(bitmap => {
!ty[polarity].every(bitmap => {
return bitmap.isEmpty();
}) &&
path.ty !== TY_ASSOCTYPE
@ -4176,18 +4195,16 @@ class DocSearch {
/** @type {PostingsList<rustdoc.QueryElement>[]} */
const results = [];
for (const [id, _name, typeData] of types) {
if (!typeData || typeData.invertedFunctionSignatureIndex.every(bitmap => {
if (!typeData || typeData[polarity].every(bitmap => {
return bitmap.isEmpty();
})) {
continue;
}
const upla = await unpackPostingsListAll(elem.generics);
const uplb = await unpackPostingsListBindings(elem.bindings);
for (const {invertedIndex: genericsIdx, queryElem: generics} of upla) {
for (const {invertedIndex: bindingsIdx, queryElem: bindings} of uplb) {
results.push({
invertedIndex: intersectInvertedIndexes(
typeData.invertedFunctionSignatureIndex,
typeData[polarity],
genericsIdx,
bindingsIdx,
),
@ -4219,15 +4236,16 @@ class DocSearch {
* take the intersection of this bitmap.
*
* @param {(rustdoc.ParserQueryElement|null)[]|null} elems
* @param {rustdoc.TypeInvertedIndexPolarity} polarity
* @returns {Promise<PostingsList<rustdoc.QueryElement[]>[]>}
*/
const unpackPostingsListAll = async elems => {
const unpackPostingsListAll = async(elems, polarity) => {
if (!elems || elems.length === 0) {
return nested_everything_postings_list;
}
const [firstPostingsList, remainingAll] = await Promise.all([
unpackPostingsList(elems[0]),
unpackPostingsListAll(elems.slice(1)),
unpackPostingsList(elems[0], polarity),
unpackPostingsListAll(elems.slice(1), polarity),
]);
/** @type {PostingsList<rustdoc.QueryElement[]>[]} */
const results = [];
@ -4261,11 +4279,12 @@ class DocSearch {
* Before passing an actual parser item to it, make sure to clone the map.
*
* @param {Map<string, rustdoc.ParserQueryElement[]>} elems
* @param {rustdoc.TypeInvertedIndexPolarity} polarity
* @returns {Promise<PostingsList<
* Map<number, rustdoc.QueryElement[]>,
* >[]>}
*/
const unpackPostingsListBindings = async elems => {
const unpackPostingsListBindings = async(elems, polarity) => {
if (!elems) {
return [{
invertedIndex: everything_inverted_index,
@ -4286,19 +4305,23 @@ class DocSearch {
queryElem: new Map(),
}];
}
const firstKeyIds = await index.search(firstKey);
// HEADS UP!
// We must put this map back the way we found it before returning,
// otherwise things break.
elems.delete(firstKey);
const [firstKeyIds, firstPostingsList, remainingAll] = await Promise.all([
index.search(firstKey),
unpackPostingsListAll(firstList, polarity),
unpackPostingsListBindings(elems, polarity),
]);
if (!firstKeyIds) {
elems.set(firstKey, firstList);
// User specified a non-existent key.
return [{
invertedIndex: empty_inverted_index,
queryElem: new Map(),
}];
}
elems.delete(firstKey);
const [firstPostingsList, remainingAll] = await Promise.all([
unpackPostingsListAll(firstList),
unpackPostingsListBindings(elems),
]);
/** @type {PostingsList<Map<number, rustdoc.QueryElement[]>>[]} */
const results = [];
for (const keyId of firstKeyIds.matches().entries()) {
@ -4335,8 +4358,8 @@ class DocSearch {
// finally, we can do the actual unification loop
const [allInputs, allOutput] = await Promise.all([
unpackPostingsListAll(inputs),
unpackPostingsListAll(output),
unpackPostingsListAll(inputs, "invertedFunctionInputsIndex"),
unpackPostingsListAll(output, "invertedFunctionOutputIndex"),
]);
let checkCounter = 0;
/**

View file

@ -71,6 +71,7 @@ static TARGETS: &[&str] = &[
"aarch64-unknown-none-softfloat",
"aarch64-unknown-redox",
"aarch64-unknown-uefi",
"aarch64-unknown-managarm-mlibc",
"amdgcn-amd-amdhsa",
"arm64e-apple-darwin",
"arm64e-apple-ios",
@ -155,6 +156,7 @@ static TARGETS: &[&str] = &[
"riscv64gc-unknown-none-elf",
"riscv64gc-unknown-linux-gnu",
"riscv64gc-unknown-linux-musl",
"riscv64gc-unknown-managarm-mlibc",
"s390x-unknown-linux-gnu",
"sparc64-unknown-linux-gnu",
"sparcv9-sun-solaris",
@ -194,6 +196,7 @@ static TARGETS: &[&str] = &[
"x86_64-unknown-redox",
"x86_64-unknown-hermit",
"x86_64-unknown-uefi",
"x86_64-unknown-managarm-mlibc",
];
/// This allows the manifest to contain rust-docs for hosts that don't build

View file

@ -6,7 +6,6 @@ use std::sync::OnceLock;
use build_helper::git::GitConfig;
use camino::{Utf8Path, Utf8PathBuf};
use semver::Version;
use serde::de::{Deserialize, Deserializer, Error as _};
use crate::executor::ColorConfig;
use crate::fatal;
@ -1072,7 +1071,7 @@ pub struct TargetCfg {
pub(crate) abi: String,
#[serde(rename = "target-family", default)]
pub(crate) families: Vec<String>,
#[serde(rename = "target-pointer-width", deserialize_with = "serde_parse_u32")]
#[serde(rename = "target-pointer-width")]
pub(crate) pointer_width: u32,
#[serde(rename = "target-endian", default)]
endian: Endian,
@ -1182,11 +1181,6 @@ fn query_rustc_output(config: &Config, args: &[&str], envs: HashMap<String, Stri
String::from_utf8(output.stdout).unwrap()
}
fn serde_parse_u32<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u32, D::Error> {
let string = String::deserialize(deserializer)?;
string.parse().map_err(D::Error::custom)
}
#[derive(Debug, Clone)]
pub struct TestPaths {
pub file: Utf8PathBuf, // e.g., compile-test/foo/bar/baz.rs

View file

@ -59,6 +59,8 @@ pub struct LintExtractor<'a> {
pub rustc_target: &'a str,
/// The target linker overriding `rustc`'s default
pub rustc_linker: Option<&'a str>,
/// Stage of the compiler that builds the docs (the stage of `rustc_path`).
pub build_rustc_stage: u32,
/// Verbose output.
pub verbose: bool,
/// Validate the style and the code example.
@ -216,14 +218,7 @@ impl<'a> LintExtractor<'a> {
if let Some(text) = line.strip_prefix("/// ") {
doc_lines.push(text.to_string());
} else if let Some(text) = line.strip_prefix("#[doc = \"") {
let escaped = text.strip_suffix("\"]").unwrap();
let mut buf = String::new();
unescape_str(escaped, |_, res| match res {
Ok(c) => buf.push(c),
Err(err) => {
assert!(!err.is_fatal(), "failed to unescape string literal")
}
});
let buf = parse_doc_string(text);
doc_lines.push(buf);
} else if line == "///" {
doc_lines.push("".to_string());
@ -234,6 +229,20 @@ impl<'a> LintExtractor<'a> {
// Ignore allow of lints (useful for
// invalid_rust_codeblocks).
continue;
} else if let Some(text) =
line.strip_prefix("#[cfg_attr(not(bootstrap), doc = \"")
{
if self.build_rustc_stage >= 1 {
let buf = parse_doc_string(text);
doc_lines.push(buf);
}
} else if let Some(text) =
line.strip_prefix("#[cfg_attr(bootstrap, doc = \"")
{
if self.build_rustc_stage == 0 {
let buf = parse_doc_string(text);
doc_lines.push(buf);
}
} else {
let name = lint_name(line).map_err(|e| {
format!(
@ -580,6 +589,23 @@ impl<'a> LintExtractor<'a> {
}
}
/// Parses a doc string that follows `#[doc = "`.
fn parse_doc_string(text: &str) -> String {
let escaped = text.strip_suffix("]").unwrap_or(text);
let escaped = escaped.strip_suffix(")").unwrap_or(escaped).strip_suffix("\"");
let Some(escaped) = escaped else {
panic!("Cannot extract docstring content from {text}");
};
let mut buf = String::new();
unescape_str(escaped, |_, res| match res {
Ok(c) => buf.push(c),
Err(err) => {
assert!(!err.is_fatal(), "failed to unescape string literal")
}
});
buf
}
/// Adds `Lint`s that have been renamed.
fn add_renamed_lints(lints: &mut Vec<Lint>) {
for (level, names) in RENAMES {

View file

@ -25,6 +25,7 @@ fn doit() -> Result<(), Box<dyn Error>> {
let mut args = std::env::args().skip(1);
let mut src_path = None;
let mut out_path = None;
let mut build_rustc_stage = None;
let mut rustc_path = None;
let mut rustc_target = None;
let mut rustc_linker = None;
@ -32,6 +33,14 @@ fn doit() -> Result<(), Box<dyn Error>> {
let mut validate = false;
while let Some(arg) = args.next() {
match arg.as_str() {
"--build-rustc-stage" => {
build_rustc_stage = match args.next() {
Some(s) => {
Some(s.parse::<u32>().expect("build rustc stage has to be an integer"))
}
None => return Err("--build-rustc-stage requires a value".into()),
};
}
"--src" => {
src_path = match args.next() {
Some(s) => Some(PathBuf::from(s)),
@ -67,6 +76,9 @@ fn doit() -> Result<(), Box<dyn Error>> {
s => return Err(format!("unexpected argument `{}`", s).into()),
}
}
if build_rustc_stage.is_none() {
return Err("--build-rustc-stage must be specified to the stage of the compiler that generates the docs".into());
}
if src_path.is_none() {
return Err("--src must be specified to the directory with the compiler source".into());
}
@ -85,6 +97,7 @@ fn doit() -> Result<(), Box<dyn Error>> {
rustc_path: &rustc_path.unwrap(),
rustc_target: &rustc_target.unwrap(),
rustc_linker: rustc_linker.as_deref(),
build_rustc_stage: build_rustc_stage.unwrap(),
verbose,
validate,
};

View file

@ -1,7 +1,7 @@
{
"llvm-target": "x86_64-unknown-none",
"target-endian": "little",
"target-pointer-width": "64",
"target-pointer-width": 64,
"target-c-int-width": 32,
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",

View file

@ -9,6 +9,7 @@ use crate::utils::io::copy_file;
/// Instruments an artifact at the given `path` (in-place) with BOLT and then calls `func`.
/// After this function finishes, the original file will be restored.
pub fn with_bolt_instrumented<F: FnOnce(&Utf8Path) -> anyhow::Result<R>, R>(
env: &Environment,
path: &Utf8Path,
func: F,
) -> anyhow::Result<R> {
@ -26,7 +27,7 @@ pub fn with_bolt_instrumented<F: FnOnce(&Utf8Path) -> anyhow::Result<R>, R>(
let profile_prefix = Utf8Path::from_path(&profile_prefix).unwrap();
// Instrument the original file with BOLT, saving the result into `instrumented_path`
cmd(&["llvm-bolt"])
cmd(&[env.llvm_bolt().as_str()])
.arg("-instrument")
.arg(path)
.arg(&format!("--instrumentation-file={profile_prefix}"))
@ -61,7 +62,7 @@ pub fn bolt_optimize(
let split_strategy =
if env.host_tuple().starts_with("aarch64") { "profile2" } else { "cdsplit" };
cmd(&["llvm-bolt"])
cmd(&[env.llvm_bolt().as_str()])
.arg(temp_path.display())
.arg("-data")
.arg(&profile.0)

View file

@ -116,6 +116,14 @@ impl Environment {
pub fn stage0(&self) -> Utf8PathBuf {
self.stage0_root.clone().unwrap_or_else(|| self.build_artifacts().join("stage0"))
}
pub fn llvm_bolt(&self) -> Utf8PathBuf {
self.host_llvm_dir().join(format!("bin/llvm-bolt{}", executable_extension()))
}
pub fn merge_fdata(&self) -> Utf8PathBuf {
self.host_llvm_dir().join(format!("bin/merge-fdata{}", executable_extension()))
}
}
/// What is the extension of binary executables on this platform?

View file

@ -329,7 +329,7 @@ fn execute_pipeline(
// FIXME(kobzol): try gather profiles together, at once for LLVM and rustc
// Instrument the libraries and gather profiles
let llvm_profile = with_bolt_instrumented(&llvm_lib, |llvm_profile_dir| {
let llvm_profile = with_bolt_instrumented(env, &llvm_lib, |llvm_profile_dir| {
stage.section("Gather profiles", |_| {
gather_bolt_profiles(env, "LLVM", llvm_benchmarks(env), llvm_profile_dir)
})
@ -354,7 +354,7 @@ fn execute_pipeline(
log::info!("Optimizing {rustc_lib} with BOLT");
// Instrument it and gather profiles
let rustc_profile = with_bolt_instrumented(&rustc_lib, |rustc_profile_dir| {
let rustc_profile = with_bolt_instrumented(env, &rustc_lib, |rustc_profile_dir| {
stage.section("Gather profiles", |_| {
gather_bolt_profiles(env, "rustc", rustc_benchmarks(env), rustc_profile_dir)
})

View file

@ -195,7 +195,8 @@ pub fn gather_bolt_profiles(
let profiles: Vec<_> =
glob::glob(&format!("{profile_prefix}*"))?.collect::<Result<Vec<_>, _>>()?;
let mut merge_args = vec!["merge-fdata"];
let fdata = env.merge_fdata();
let mut merge_args = vec![fdata.as_str()];
merge_args.extend(profiles.iter().map(|p| p.to_str().unwrap()));
with_log_group("Merging BOLT profiles", || {

View file

@ -92,7 +92,6 @@ static PROPERTIES: &[&str] = &[
"Case_Ignorable",
"Grapheme_Extend",
"White_Space",
"Cc",
"N",
];

View file

@ -52,6 +52,9 @@
//@ revisions: aarch64_unknown_linux_ohos
//@ [aarch64_unknown_linux_ohos] compile-flags: --target aarch64-unknown-linux-ohos
//@ [aarch64_unknown_linux_ohos] needs-llvm-components: aarch64
//@ revisions: aarch64_unknown_managarm_mlibc
//@ [aarch64_unknown_managarm_mlibc] compile-flags: --target aarch64-unknown-managarm-mlibc
//@ [aarch64_unknown_managarm_mlibc] needs-llvm-components: aarch64
//@ revisions: aarch64_unknown_netbsd
//@ [aarch64_unknown_netbsd] compile-flags: --target aarch64-unknown-netbsd
//@ [aarch64_unknown_netbsd] needs-llvm-components: aarch64
@ -490,6 +493,9 @@
//@ revisions: riscv64gc_unknown_linux_musl
//@ [riscv64gc_unknown_linux_musl] compile-flags: --target riscv64gc-unknown-linux-musl
//@ [riscv64gc_unknown_linux_musl] needs-llvm-components: riscv
//@ revisions: riscv64gc_unknown_managarm_mlibc
//@ [riscv64gc_unknown_managarm_mlibc] compile-flags: --target riscv64gc-unknown-managarm-mlibc
//@ [riscv64gc_unknown_managarm_mlibc] needs-llvm-components: riscv
//@ revisions: riscv64gc_unknown_netbsd
//@ [riscv64gc_unknown_netbsd] compile-flags: --target riscv64gc-unknown-netbsd
//@ [riscv64gc_unknown_netbsd] needs-llvm-components: riscv
@ -649,6 +655,9 @@
//@ revisions: x86_64_unknown_linux_none
//@ [x86_64_unknown_linux_none] compile-flags: --target x86_64-unknown-linux-none
//@ [x86_64_unknown_linux_none] needs-llvm-components: x86
//@ revisions: x86_64_unknown_managarm_mlibc
//@ [x86_64_unknown_managarm_mlibc] compile-flags: --target x86_64-unknown-managarm-mlibc
//@ [x86_64_unknown_managarm_mlibc] needs-llvm-components: x86
//@ revisions: x86_64_unknown_netbsd
//@ [x86_64_unknown_netbsd] compile-flags: --target x86_64-unknown-netbsd
//@ [x86_64_unknown_netbsd] needs-llvm-components: x86

View file

@ -1,7 +1,6 @@
//@ compile-flags: -Copt-level=3
#![crate_type = "lib"]
#![feature(array_repeat)]
use std::array::repeat;

View file

@ -4,7 +4,6 @@
#![crate_type = "lib"]
#![feature(iter_repeat_n)]
#![feature(array_repeat)]
#[derive(Clone)]
pub struct NotCopy(u16);

View file

@ -53,5 +53,5 @@
"target-family": [
"unix"
],
"target-pointer-width": "64"
"target-pointer-width": 64
}

View file

@ -33,5 +33,5 @@
"thread"
],
"target-family": "unix",
"target-pointer-width": "64"
"target-pointer-width": 64
}

View file

@ -4,7 +4,7 @@
"linker-flavor": "gcc",
"llvm-target": "x86_64-unknown-linux-gnu",
"target-endian": "big",
"target-pointer-width": "64",
"target-pointer-width": 64,
"arch": "x86_64",
"os": "linux"
}

View file

@ -2,5 +2,5 @@
"arch": "x86_64",
"data-layout": "e-m:e-i64:16:32:64",
"llvm-target": "x86_64-unknown-unknown-gnu",
"target-pointer-width": "64"
"target-pointer-width": 64
}

View file

@ -3,7 +3,7 @@
"linker-flavor": "gcc",
"llvm-target": "i686-unknown-linux-gnu",
"target-endian": "little",
"target-pointer-width": "32",
"target-pointer-width": 32,
"arch": "x86",
"os": "linux"
}

View file

@ -2,7 +2,7 @@
"data-layout": "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32",
"linker-flavor": "gcc",
"target-endian": "little",
"target-pointer-width": "32",
"target-pointer-width": 32,
"arch": "x86",
"os": "foo"
}

View file

@ -4,7 +4,7 @@
"linker-flavor": "gcc",
"llvm-target": "x86_64-unknown-linux-gnu",
"target-endian": "little",
"target-pointer-width": "64",
"target-pointer-width": 64,
"arch": "x86_64",
"os": "linux"
}

View file

@ -3,7 +3,7 @@
"linker-flavor": "gcc",
"llvm-target": "i686-unknown-linux-gnu",
"target-endian": "little",
"target-pointer-width": "32",
"target-pointer-width": 32,
"arch": "x86",
"os": "linux",
"need-explicit-cpu": true

View file

@ -20,6 +20,7 @@
target_os = "nto",
target_os = "openbsd",
target_os = "fuchsia",
target_os = "managarm",
),
link_section = ".init_array"
)]

View file

@ -24,7 +24,7 @@ warning: unexpected `cfg` condition value: `does_not_exist`
LL | #![cfg(not(target(os = "does_not_exist")))]
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, and `uefi` and 10 more
= note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, and `tvos` and 11 more
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
= note: `#[warn(unexpected_cfgs)]` on by default

View file

@ -3,7 +3,7 @@
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
"target-pointer-width": 64,
"os": "ericos",
"linker-flavor": "ld.lld",
"linker": "rust-lld",

View file

@ -156,7 +156,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`
LL | target_env = "_UNEXPECTED_VALUE",
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: expected values for `target_env` are: ``, `gnu`, `macabi`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5`
= note: expected values for `target_env` are: ``, `gnu`, `macabi`, `mlibc`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`
@ -201,7 +201,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`
LL | target_os = "_UNEXPECTED_VALUE",
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm`
= note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`
@ -274,7 +274,7 @@ LL | #[cfg(target_os = "linuz")] // testing that we suggest `linux`
| |
| help: there is a expected value with a similar name: `"linux"`
|
= note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm`
= note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
warning: 28 warnings emitted

View file

@ -3,7 +3,7 @@
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
"target-pointer-width": 64,
"os": "none",
"linker-flavor": "ld.lld",
"linker": "rust-lld",

View file

@ -13,9 +13,11 @@ fn main() {
format!("{?:#?}", bar);
//~^ ERROR invalid format string: expected format parameter to occur after `:`
format!("Hello {<5:}!", "x");
//~^ ERROR invalid format string: expected format parameter to occur after `:`
//~^ ERROR invalid format string: expected alignment specifier after `:` in format string; example: `{:>?}`
format!("Hello {^5:}!", "x");
//~^ ERROR invalid format string: expected format parameter to occur after `:`
//~^ ERROR invalid format string: expected alignment specifier after `:` in format string; example: `{:>?}`
format!("Hello {>5:}!", "x");
//~^ ERROR invalid format string: expected format parameter to occur after `:`
//~^ ERROR invalid format string: expected alignment specifier after `:` in format string; example: `{:>?}`
println!("{0:#X>18}", 12345);
//~^ ERROR invalid format string: expected alignment specifier after `:` in format string; example: `{:>?}`
}

View file

@ -50,23 +50,29 @@ LL | format!("{?:#?}", bar);
|
= note: `?` comes after `:`, try `:?` instead
error: invalid format string: expected format parameter to occur after `:`
error: invalid format string: expected alignment specifier after `:` in format string; example: `{:>?}`
--> $DIR/format-string-wrong-order.rs:15:21
|
LL | format!("Hello {<5:}!", "x");
| ^ expected `<` to occur after `:` in format string
error: invalid format string: expected format parameter to occur after `:`
error: invalid format string: expected alignment specifier after `:` in format string; example: `{:>?}`
--> $DIR/format-string-wrong-order.rs:17:21
|
LL | format!("Hello {^5:}!", "x");
| ^ expected `^` to occur after `:` in format string
error: invalid format string: expected format parameter to occur after `:`
error: invalid format string: expected alignment specifier after `:` in format string; example: `{:>?}`
--> $DIR/format-string-wrong-order.rs:19:21
|
LL | format!("Hello {>5:}!", "x");
| ^ expected `>` to occur after `:` in format string
error: aborting due to 9 previous errors
error: invalid format string: expected alignment specifier after `:` in format string; example: `{:>?}`
--> $DIR/format-string-wrong-order.rs:21:20
|
LL | println!("{0:#X>18}", 12345);
| ^ expected `>` to occur after `:` in format string
error: aborting due to 10 previous errors

View file

@ -0,0 +1,11 @@
// Regression test for issue #146045 - ensure that the TAIT `SplitPaths` does not
// require the borrowed string to be live.
//@ check-pass
//@ edition:2015
pub fn repro() -> Option<std::path::PathBuf> {
let unparsed = std::ffi::OsString::new();
std::env::split_paths(&unparsed).next()
}
fn main() {}

View file

@ -0,0 +1,24 @@
fn a() {
let Some(x) = foo() { //~ ERROR expected one of
//~^ HELP you might have meant to use `if let`
let y = x;
}
}
fn b() {
let Some(x) = foo() { //~ ERROR expected one of
//~^ HELP you might have meant to use `let else`
return;
}
}
fn c() {
let Some(x) = foo() { //~ ERROR expected one of
//~^ HELP you might have meant to use `if let`
//~| HELP alternatively, you might have meant to use `let else`
// The parser check happens pre-macro-expansion, so we don't know for sure.
println!("{x}");
}
}
fn foo() -> Option<i32> {
Some(42)
}
fn main() {}

View file

@ -0,0 +1,39 @@
error: expected one of `.`, `;`, `?`, `else`, or an operator, found `{`
--> $DIR/missing-if-let-or-let-else.rs:2:25
|
LL | let Some(x) = foo() {
| ^ expected one of `.`, `;`, `?`, `else`, or an operator
|
help: you might have meant to use `if let`
|
LL | if let Some(x) = foo() {
| ++
error: expected one of `.`, `;`, `?`, `else`, or an operator, found `{`
--> $DIR/missing-if-let-or-let-else.rs:8:25
|
LL | let Some(x) = foo() {
| ^ expected one of `.`, `;`, `?`, `else`, or an operator
|
help: you might have meant to use `let else`
|
LL | let Some(x) = foo() else {
| ++++
error: expected one of `.`, `;`, `?`, `else`, or an operator, found `{`
--> $DIR/missing-if-let-or-let-else.rs:14:25
|
LL | let Some(x) = foo() {
| ^ expected one of `.`, `;`, `?`, `else`, or an operator
|
help: you might have meant to use `if let`
|
LL | if let Some(x) = foo() {
| ++
help: alternatively, you might have meant to use `let else`
|
LL | let Some(x) = foo() else {
| ++++
error: aborting due to 3 previous errors