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:
commit
4cc7ccaf98
99 changed files with 2688 additions and 993 deletions
|
|
@ -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 },
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -22,5 +22,5 @@
|
|||
"unix"
|
||||
],
|
||||
"target-mcount": "_mcount",
|
||||
"target-pointer-width": "32"
|
||||
"target-pointer-width": 32
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}}
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
17
compiler/rustc_target/src/spec/base/managarm_mlibc.rs
Normal file
17
compiler/rustc_target/src/spec/base/managarm_mlibc.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
"#;
|
||||
|
|
|
|||
|
|
@ -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 '$'
|
||||
//! ```
|
||||
|
|
|
|||
|
|
@ -19,5 +19,5 @@
|
|||
},
|
||||
"panic-strategy": "abort",
|
||||
"relocation-model": "static",
|
||||
"target-pointer-width": "32"
|
||||
"target-pointer-width": 32
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
448
library/std/src/sync/nonpoison/condvar.rs
Normal file
448
library/std/src/sync/nonpoison/condvar.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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
4
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(", ");
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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`.",
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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` | ✓ | |
|
||||
|
|
|
|||
53
src/doc/rustc/src/platform-support/managarm.md
Normal file
53
src/doc/rustc/src/platform-support/managarm.md
Normal 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.
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
5
src/librustdoc/html/static/js/rustdoc.d.ts
vendored
5
src/librustdoc/html/static/js/rustdoc.d.ts
vendored
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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", || {
|
||||
|
|
|
|||
|
|
@ -92,7 +92,6 @@ static PROPERTIES: &[&str] = &[
|
|||
"Case_Ignorable",
|
||||
"Grapheme_Extend",
|
||||
"White_Space",
|
||||
"Cc",
|
||||
"N",
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
//@ compile-flags: -Copt-level=3
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(array_repeat)]
|
||||
|
||||
use std::array::repeat;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(iter_repeat_n)]
|
||||
#![feature(array_repeat)]
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NotCopy(u16);
|
||||
|
|
|
|||
|
|
@ -53,5 +53,5 @@
|
|||
"target-family": [
|
||||
"unix"
|
||||
],
|
||||
"target-pointer-width": "64"
|
||||
"target-pointer-width": 64
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,5 +33,5 @@
|
|||
"thread"
|
||||
],
|
||||
"target-family": "unix",
|
||||
"target-pointer-width": "64"
|
||||
"target-pointer-width": 64
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
target_os = "nto",
|
||||
target_os = "openbsd",
|
||||
target_os = "fuchsia",
|
||||
target_os = "managarm",
|
||||
),
|
||||
link_section = ".init_array"
|
||||
)]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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: `{:>?}`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
11
tests/ui/type-alias-impl-trait/split-paths-may-dangle.rs
Normal file
11
tests/ui/type-alias-impl-trait/split-paths-may-dangle.rs
Normal 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() {}
|
||||
24
tests/ui/uninhabited/missing-if-let-or-let-else.rs
Normal file
24
tests/ui/uninhabited/missing-if-let-or-let-else.rs
Normal 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() {}
|
||||
39
tests/ui/uninhabited/missing-if-let-or-let-else.stderr
Normal file
39
tests/ui/uninhabited/missing-if-let-or-let-else.stderr
Normal 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
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue