diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 6a8da0305464a..4e850810a30a9 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -220,6 +220,11 @@ # which is also used in places like debuginfo `DW_AT_producer`. This may be useful for # supplementary build information, like distro-specific package versions. # +# IMPORTANT: Changing this value changes crate IDs and symbol name mangling, making +# compiled artifacts incompatible. PGO profiles cannot be reused across different +# descriptions, and incremental compilation caches are invalidated. Keep this value +# consistent when reusing build artifacts. +# # The Rust compiler will differentiate between versions of itself, including # based on this string, which means that if you wish to be compatible with # upstream Rust you need to set this to "". However, note that if you set this to "" but diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 6dff79374f20f..5a49d0f07a490 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -86,7 +86,8 @@ impl MetadataLoader for DefaultMetadataLoader { format!("failed to parse aix dylib '{}': {}", path.display(), e) })?; - match archive.members().exactly_one() { + // FIXME: rewrite in terms of `#![feature(exact_length_collection)]`. See: #149266 + match Itertools::exactly_one(archive.members()) { Ok(lib) => { let lib = lib.map_err(|e| { format!("failed to parse aix dylib '{}': {}", path.display(), e) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index c6c93ed7f69ec..fa5041daa69e9 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -36,21 +36,21 @@ enum MulAddType { #[derive(Copy, Clone)] pub(crate) enum MinMax { - /// The IEEE `Minimum` operation - see `f32::minimum` etc + /// The IEEE-2019 `minimum` operation - see `f32::minimum` etc. /// In particular, `-0.0` is considered smaller than `+0.0` and /// if either input is NaN, the result is NaN. Minimum, - /// The IEEE `MinNum` operation - see `f32::min` etc + /// The IEEE-2008 `minNum` operation - see `f32::min` etc. /// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic, - /// and is one argument is NaN, the other one is returned. + /// and if one argument is NaN, the other one is returned. MinNum, - /// The IEEE `Maximum` operation - see `f32::maximum` etc + /// The IEEE-2019 `maximum` operation - see `f32::maximum` etc. /// In particular, `-0.0` is considered smaller than `+0.0` and /// if either input is NaN, the result is NaN. Maximum, - /// The IEEE `MaxNum` operation - see `f32::max` etc + /// The IEEE-2008 `maxNum` operation - see `f32::max` etc. /// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic, - /// and is one argument is NaN, the other one is returned. + /// and if one argument is NaN, the other one is returned. MaxNum, } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 47ecc71d8bf5b..6eefb2f48d127 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1586,9 +1586,10 @@ pub static BUILTIN_ATTRIBUTE_MAP: LazyLock> map }); -pub fn is_stable_diagnostic_attribute(sym: Symbol, _features: &Features) -> bool { +pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool { match sym { sym::on_unimplemented | sym::do_not_recommend => true, + sym::on_const => features.diagnostic_on_const(), _ => false, } } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 813d2fced865d..cc1dfb72142f1 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -483,6 +483,8 @@ declare_features! ( (incomplete, deref_patterns, "1.79.0", Some(87121)), /// Allows deriving the From trait on single-field structs. (unstable, derive_from, "1.91.0", Some(144889)), + /// Allows giving non-const impls custom diagnostic messages if attempted to be used as const + (unstable, diagnostic_on_const, "CURRENT_RUSTC_VERSION", Some(143874)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), /// Allows `#[doc(masked)]`. diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index e124dfeb72840..beba0165549e0 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -802,6 +802,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().type_of(def_id); tcx.ensure_ok().predicates_of(def_id); tcx.ensure_ok().associated_items(def_id); + check_diagnostic_attrs(tcx, def_id); if of_trait { let impl_trait_header = tcx.impl_trait_header(def_id); res = res.and( @@ -824,7 +825,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().predicates_of(def_id); tcx.ensure_ok().associated_items(def_id); let assoc_items = tcx.associated_items(def_id); - check_on_unimplemented(tcx, def_id); + check_diagnostic_attrs(tcx, def_id); for &assoc_item in assoc_items.in_definition_order() { match assoc_item.kind { @@ -1112,7 +1113,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), }) } -pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, def_id: LocalDefId) { +pub(super) fn check_diagnostic_attrs(tcx: TyCtxt<'_>, def_id: LocalDefId) { // an error would be reported if this fails. let _ = OnUnimplementedDirective::of_item(tcx, def_id.to_def_id()); } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 44177d4ae468c..91a20a12b6334 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -96,6 +96,9 @@ passes_deprecated_annotation_has_no_effect = passes_deprecated_attribute = deprecated attribute must be paired with either stable or unstable attribute +passes_diagnostic_diagnostic_on_const_only_for_trait_impls = + `#[diagnostic::on_const]` can only be applied to trait impls + passes_diagnostic_diagnostic_on_unimplemented_only_for_traits = `#[diagnostic::on_unimplemented]` can only be applied to trait definitions diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 58e55211c671c..cba600594cda4 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -23,8 +23,8 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem, HirId, Item, - ItemKind, MethodKind, PartialConstStability, Safety, Stability, StabilityLevel, Target, + self as hir, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, Constness, FnSig, ForeignItem, HirId, + Item, ItemKind, MethodKind, PartialConstStability, Safety, Stability, StabilityLevel, Target, TraitItem, find_attr, }; use rustc_macros::LintDiagnostic; @@ -55,6 +55,10 @@ use crate::{errors, fluent_generated as fluent}; #[diag(passes_diagnostic_diagnostic_on_unimplemented_only_for_traits)] struct DiagnosticOnUnimplementedOnlyForTraits; +#[derive(LintDiagnostic)] +#[diag(passes_diagnostic_diagnostic_on_const_only_for_trait_impls)] +struct DiagnosticOnConstOnlyForTraitImpls; + fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, @@ -294,6 +298,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::diagnostic, sym::on_unimplemented, ..] => { self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) } + [sym::diagnostic, sym::on_const, ..] => { + self.check_diagnostic_on_const(attr.span(), hir_id, target, item) + } [sym::thread_local, ..] => self.check_thread_local(attr, span, target), [sym::doc, ..] => self.check_doc_attrs( attr, @@ -517,6 +524,31 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + /// Checks if `#[diagnostic::on_const]` is applied to a trait impl + fn check_diagnostic_on_const( + &self, + attr_span: Span, + hir_id: HirId, + target: Target, + item: Option>, + ) { + if matches!(target, Target::Impl { of_trait: true }) { + match item.unwrap() { + ItemLike::Item(it) => match it.expect_impl().constness { + Constness::Const => {} + Constness::NotConst => return, + }, + ItemLike::ForeignItem => {} + } + } + self.tcx.emit_node_span_lint( + MISPLACED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + DiagnosticOnConstOnlyForTraitImpls, + ); + } + /// Checks if an `#[inline]` is applied to a function or a closure. fn check_inline(&self, hir_id: HirId, attr_span: Span, kind: &InlineAttr, target: Target) { match target { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 828759e2472f1..1222b40d8b367 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -690,10 +690,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && let [namespace, attribute, ..] = &*path.segments && namespace.ident.name == sym::diagnostic - && ![sym::on_unimplemented, sym::do_not_recommend].contains(&attribute.ident.name) + && ![sym::on_unimplemented, sym::do_not_recommend, sym::on_const] + .contains(&attribute.ident.name) { let typo_name = find_best_match_for_name( - &[sym::on_unimplemented, sym::do_not_recommend], + &[sym::on_unimplemented, sym::do_not_recommend, sym::on_const], attribute.ident.name, Some(5), ); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b3959c53a35e5..f2b13dad1fd90 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -878,6 +878,7 @@ symbols! { destructuring_assignment, diagnostic, diagnostic_namespace, + diagnostic_on_const, dialect, direct, discriminant_kind, @@ -1594,6 +1595,7 @@ symbols! { old_name, omit_gdb_pretty_printer_section, on, + on_const, on_unimplemented, opaque, opaque_module_name_placeholder: "", diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index c8cba30dde051..c5173179eecf2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -43,6 +43,7 @@ use super::{ }; use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::TyCategory; +use crate::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; use crate::error_reporting::traits::report_dyn_incompatibility; use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, CoroClosureNotFn}; use crate::infer::{self, InferCtxt, InferCtxtExt as _}; @@ -587,7 +588,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => { - self.report_host_effect_error(bound_predicate.rebind(predicate), obligation.param_env, span) + self.report_host_effect_error(bound_predicate.rebind(predicate), &obligation, span) } ty::PredicateKind::Subtype(predicate) => { @@ -808,20 +809,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn report_host_effect_error( &self, predicate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, - param_env: ty::ParamEnv<'tcx>, + main_obligation: &PredicateObligation<'tcx>, span: Span, ) -> Diag<'a> { // FIXME(const_trait_impl): We should recompute the predicate with `[const]` // if it's `const`, and if it holds, explain that this bound only - // *conditionally* holds. If that fails, we should also do selection - // to drill this down to an impl or built-in source, so we can - // point at it and explain that while the trait *is* implemented, - // that implementation is not const. + // *conditionally* holds. let trait_ref = predicate.map_bound(|predicate| ty::TraitPredicate { trait_ref: predicate.trait_ref, polarity: ty::PredicatePolarity::Positive, }); let mut file = None; + let err_msg = self.get_standard_error_message( trait_ref, None, @@ -832,18 +831,21 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); let mut diag = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg); *diag.long_ty_path() = file; - if !self.predicate_may_hold(&Obligation::new( + let obligation = Obligation::new( self.tcx, ObligationCause::dummy(), - param_env, + main_obligation.param_env, trait_ref, - )) { + ); + if !self.predicate_may_hold(&obligation) { diag.downgrade_to_delayed_bug(); } - for candidate in self.find_similar_impl_candidates(trait_ref) { - let CandidateSimilarity::Exact { .. } = candidate.similarity else { continue }; - let impl_did = candidate.impl_def_id; - let trait_did = candidate.trait_ref.def_id; + + if let Ok(Some(ImplSource::UserDefined(impl_data))) = + SelectionContext::new(self).select(&obligation.with(self.tcx, trait_ref.skip_binder())) + { + let impl_did = impl_data.impl_def_id; + let trait_did = trait_ref.def_id(); let impl_span = self.tcx.def_span(impl_did); let trait_name = self.tcx.item_name(trait_did); @@ -865,6 +867,42 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { impl_span, format!("trait `{trait_name}` is implemented but not `const`"), ); + + let (condition_options, format_args) = self.on_unimplemented_components( + trait_ref, + main_obligation, + diag.long_ty_path(), + ); + + if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, impl_did) + { + let note = command.evaluate( + self.tcx, + predicate.skip_binder().trait_ref, + &condition_options, + &format_args, + ); + let OnUnimplementedNote { + message, + label, + notes, + parent_label, + append_const_msg: _, + } = note; + + if let Some(message) = message { + diag.primary_message(message); + } + if let Some(label) = label { + diag.span_label(impl_span, label); + } + for note in notes { + diag.note(note); + } + if let Some(parent_label) = parent_label { + diag.span_label(impl_span, parent_label); + } + } } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index a5cb374ea0eb1..0a6af42e122b6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -5,6 +5,7 @@ use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit}; use rustc_errors::codes::*; use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{AttrArgs, Attribute}; use rustc_macros::LintDiagnostic; @@ -103,7 +104,27 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if trait_pred.polarity() != ty::PredicatePolarity::Positive { return OnUnimplementedNote::default(); } + let (condition_options, format_args) = + self.on_unimplemented_components(trait_pred, obligation, long_ty_path); + if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, trait_pred.def_id()) + { + command.evaluate( + self.tcx, + trait_pred.skip_binder().trait_ref, + &condition_options, + &format_args, + ) + } else { + OnUnimplementedNote::default() + } + } + pub(crate) fn on_unimplemented_components( + &self, + trait_pred: ty::PolyTraitPredicate<'tcx>, + obligation: &PredicateObligation<'tcx>, + long_ty_path: &mut Option, + ) -> (ConditionOptions, FormatArgs<'tcx>) { let (def_id, args) = self .impl_similar_to(trait_pred, obligation) .unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args)); @@ -293,12 +314,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .collect(); let format_args = FormatArgs { this, trait_sugared, generic_args, item_context }; - - if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) { - command.evaluate(self.tcx, trait_pred.trait_ref, &condition_options, &format_args) - } else { - OnUnimplementedNote::default() - } + (condition_options, format_args) } } @@ -325,7 +341,7 @@ pub struct OnUnimplementedDirective { } /// For the `#[rustc_on_unimplemented]` attribute -#[derive(Default)] +#[derive(Default, Debug)] pub struct OnUnimplementedNote { pub message: Option, pub label: Option, @@ -562,17 +578,21 @@ impl<'tcx> OnUnimplementedDirective { } pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result, ErrorGuaranteed> { - if !tcx.is_trait(item_def_id) { + let attr = if tcx.is_trait(item_def_id) { + sym::on_unimplemented + } else if let DefKind::Impl { of_trait: true } = tcx.def_kind(item_def_id) { + sym::on_const + } else { // It could be a trait_alias (`trait MyTrait = SomeOtherTrait`) // or an implementation (`impl MyTrait for Foo {}`) // // We don't support those. return Ok(None); - } + }; if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) { return Self::parse_attribute(attr, false, tcx, item_def_id); } else { - tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented]) + tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, attr]) .filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose()) .try_fold(None, |aggr: Option, directive| { let directive = directive?; diff --git a/library/Cargo.lock b/library/Cargo.lock index 8f60cea459c7d..accbbe9d236ff 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -175,9 +175,9 @@ dependencies = [ [[package]] name = "moto-rt" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058a2807a30527bee4c30df7ababe971cdde94372d4dbd1ff145bb403381436c" +checksum = "0bf4bc387d3b3502cb92c09ec980cca909b94978e144c61da8319ecf4bc8d031" dependencies = [ "rustc-std-workspace-alloc", "rustc-std-workspace-core", diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 4b1e013c2b445..7a80023ce64eb 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -625,7 +625,7 @@ impl<'a> Formatter<'a> { // // 2) Placeholders representation (e.g. format_args!("hello {name}\n")) // ┌────────────────────────────────┐ -// template: │ *const u8 │ ─▷ b"\x06hello \x80\x01\n\x00" +// template: │ *const u8 │ ─▷ b"\x06hello \xC0\x01\n\x00" // ├────────────────────────────────┤ // args: │ &'a [Argument<'a>; _] 0│ (lower bit is 0 due to alignment of Argument type) // └────────────────────────────────┘ diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 9d99b05b38d69..2115c5c9a85d8 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2947,61 +2947,77 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize); #[rustc_intrinsic] pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize); -/// Returns the minimum (IEEE 754-2008 minNum) of two `f16` values. +/// Returns the minimum of two `f16` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 minNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f16::min`] +/// The stabilized version of this intrinsic is [`f16::min`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn minnumf16(x: f16, y: f16) -> f16; -/// Returns the minimum (IEEE 754-2008 minNum) of two `f32` values. +/// Returns the minimum of two `f32` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 minNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f32::min`] +/// The stabilized version of this intrinsic is [`f32::min`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn minnumf32(x: f32, y: f32) -> f32; -/// Returns the minimum (IEEE 754-2008 minNum) of two `f64` values. +/// Returns the minimum of two `f64` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 minNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f64::min`] +/// The stabilized version of this intrinsic is [`f64::min`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn minnumf64(x: f64, y: f64) -> f64; -/// Returns the minimum (IEEE 754-2008 minNum) of two `f128` values. +/// Returns the minimum of two `f128` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 minNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f128::min`] +/// The stabilized version of this intrinsic is [`f128::min`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn minnumf128(x: f128, y: f128) -> f128; -/// Returns the minimum (IEEE 754-2019 minimum) of two `f16` values. +/// Returns the minimum of two `f16` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3022,7 +3038,11 @@ pub const fn minimumf16(x: f16, y: f16) -> f16 { } } -/// Returns the minimum (IEEE 754-2019 minimum) of two `f32` values. +/// Returns the minimum of two `f32` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3043,7 +3063,11 @@ pub const fn minimumf32(x: f32, y: f32) -> f32 { } } -/// Returns the minimum (IEEE 754-2019 minimum) of two `f64` values. +/// Returns the minimum of two `f64` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3064,7 +3088,11 @@ pub const fn minimumf64(x: f64, y: f64) -> f64 { } } -/// Returns the minimum (IEEE 754-2019 minimum) of two `f128` values. +/// Returns the minimum of two `f128` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3085,61 +3113,77 @@ pub const fn minimumf128(x: f128, y: f128) -> f128 { } } -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f16` values. +/// Returns the maximum of two `f16` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 maxNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f16::max`] +/// The stabilized version of this intrinsic is [`f16::max`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn maxnumf16(x: f16, y: f16) -> f16; -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f32` values. +/// Returns the maximum of two `f32` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 maxNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f32::max`] +/// The stabilized version of this intrinsic is [`f32::max`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn maxnumf32(x: f32, y: f32) -> f32; -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f64` values. +/// Returns the maximum of two `f64` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 maxNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f64::max`] +/// The stabilized version of this intrinsic is [`f64::max`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn maxnumf64(x: f64, y: f64) -> f64; -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f128` values. +/// Returns the maximum of two `f128` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 maxNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f128::max`] +/// The stabilized version of this intrinsic is [`f128::max`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn maxnumf128(x: f128, y: f128) -> f128; -/// Returns the maximum (IEEE 754-2019 maximum) of two `f16` values. +/// Returns the maximum of two `f16` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3159,7 +3203,11 @@ pub const fn maximumf16(x: f16, y: f16) -> f16 { } } -/// Returns the maximum (IEEE 754-2019 maximum) of two `f32` values. +/// Returns the maximum of two `f32` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3179,7 +3227,11 @@ pub const fn maximumf32(x: f32, y: f32) -> f32 { } } -/// Returns the maximum (IEEE 754-2019 maximum) of two `f64` values. +/// Returns the maximum of two `f64` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3199,7 +3251,11 @@ pub const fn maximumf64(x: f64, y: f64) -> f64 { } } -/// Returns the maximum (IEEE 754-2019 maximum) of two `f128` values. +/// Returns the maximum of two `f128` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 29230b1665380..f898382086512 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -4034,6 +4034,62 @@ pub trait Iterator { { unreachable!("Always specialized"); } + + /// Checks if the iterator contains *exactly* one element. + /// If so, returns this one element. + /// + /// See also [`collect_array`](Iterator::collect_array) for lengths other than `1`. + /// + /// # Examples + /// + /// ``` + /// #![feature(exact_length_collection)] + /// + /// assert_eq!([1].into_iter().exactly_one(), Some(1)); + /// assert_eq!([].into_iter().exactly_one(), None::<()>); + /// + /// // There is exactly one even integer in the array: + /// assert_eq!([1, 2, 3].into_iter().filter(|x| x % 2 == 0).exactly_one(), Some(2)); + /// // But there are two odds, which is too many: + /// assert_eq!([1, 2, 3].into_iter().filter(|x| x % 2 == 1).exactly_one(), None); + /// ``` + #[inline] + #[unstable(feature = "exact_length_collection", issue = "149266")] + fn exactly_one(self) -> Option + where + Self: Sized, + { + self.collect_array::<1>().map(|[i]| i) + } + + /// Checks if an iterator has *exactly* `N` elements. + /// If so, returns those `N` elements in an array. + /// + /// See also [`exactly_one`](Iterator::exactly_one) when expecting a single element. + /// + /// # Examples + /// + /// ``` + /// #![feature(exact_length_collection)] + /// + /// assert_eq!([1, 2, 3, 4].into_iter().collect_array(), Some([1, 2, 3, 4])); + /// assert_eq!([1, 2].into_iter().chain([3, 4]).collect_array(), Some([1, 2, 3, 4])); + /// + /// // Iterator contains too few elements: + /// assert_eq!([1, 2].into_iter().collect_array::<4>(), None); + /// // Iterator contains too many elements: + /// assert_eq!([1, 2, 3, 4, 5].into_iter().collect_array::<4>(), None); + /// // Taking 4 makes it work again: + /// assert_eq!([1, 2, 3, 4, 5].into_iter().take(4).collect_array(), Some([1, 2, 3, 4])); + /// ``` + #[inline] + #[unstable(feature = "exact_length_collection", issue = "149266")] + fn collect_array(mut self) -> Option<[Self::Item; N]> + where + Self: Sized, + { + self.next_chunk().ok().filter(|_| self.next().is_none()) + } } trait SpecIterEq: Iterator { diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index ef85e36900869..a1c7990f2311d 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -149,6 +149,7 @@ #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(derive_const)] +#![feature(diagnostic_on_const)] #![feature(doc_cfg)] #![feature(doc_notable_trait)] #![feature(extern_types)] diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 3507d1a0a9a8c..aee28c4590c29 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1128,7 +1128,6 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// /// let mut dst = [MaybeUninit::uninit(); 32]; @@ -1140,8 +1139,6 @@ impl [MaybeUninit] { /// ``` /// /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// /// let mut vec = Vec::with_capacity(32); /// let src = [0; 16]; /// @@ -1157,7 +1154,8 @@ impl [MaybeUninit] { /// ``` /// /// [`write_clone_of_slice`]: slice::write_clone_of_slice - #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[stable(feature = "maybe_uninit_write_slice", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "maybe_uninit_write_slice", since = "CURRENT_RUSTC_VERSION")] pub const fn write_copy_of_slice(&mut self, src: &[T]) -> &mut [T] where T: Copy, @@ -1188,7 +1186,6 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// /// let mut dst = [const { MaybeUninit::uninit() }; 5]; @@ -1203,8 +1200,6 @@ impl [MaybeUninit] { /// ``` /// /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// /// let mut vec = Vec::with_capacity(32); /// let src = ["rust", "is", "a", "pretty", "cool", "language"].map(|s| s.to_string()); /// @@ -1220,7 +1215,7 @@ impl [MaybeUninit] { /// ``` /// /// [`write_copy_of_slice`]: slice::write_copy_of_slice - #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[stable(feature = "maybe_uninit_write_slice", since = "CURRENT_RUSTC_VERSION")] pub fn write_clone_of_slice(&mut self, src: &[T]) -> &mut [T] where T: Clone, @@ -1415,7 +1410,7 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes, maybe_uninit_write_slice, maybe_uninit_slice)] + /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] /// use std::mem::MaybeUninit; /// /// let uninit = [MaybeUninit::new(0x1234u16), MaybeUninit::new(0x5678u16)]; @@ -1442,7 +1437,7 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes, maybe_uninit_write_slice, maybe_uninit_slice)] + /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] /// use std::mem::MaybeUninit; /// /// let mut uninit = [MaybeUninit::::uninit(), MaybeUninit::::uninit()]; diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 2cf06b6d6a35a..c83455c5870bb 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -709,6 +709,7 @@ impl f128 { /// let y = 2.0f128; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f128::NAN), x); /// # } /// ``` #[inline] @@ -736,6 +737,7 @@ impl f128 { /// let y = 2.0f128; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f128::NAN), x); /// # } /// ``` #[inline] diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 51f803672e5c6..e7da93cc7318d 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -701,6 +701,7 @@ impl f16 { /// let y = 2.0f16; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f16::NAN), x); /// # } /// ``` #[inline] @@ -727,6 +728,7 @@ impl f16 { /// let y = 2.0f16; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f16::NAN), x); /// # } /// ``` #[inline] diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 3070e1dedbe43..0e562c0c8fe53 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -908,6 +908,7 @@ impl f32 { /// let y = 2.0f32; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f32::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] @@ -930,6 +931,7 @@ impl f32 { /// let y = 2.0f32; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f32::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index dc8ccc551b2da..04f26affeae55 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -926,6 +926,7 @@ impl f64 { /// let y = 2.0_f64; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f64::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] @@ -948,6 +949,7 @@ impl f64 { /// let y = 2.0_f64; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f64::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 93fdf2823aeb7..21f9be8fc7695 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -372,6 +372,8 @@ macro_rules! int_impl { /// /// On big endian this is a no-op. On little endian the bytes are swapped. /// + /// See also [from_be_bytes()](Self::from_be_bytes). + /// /// # Examples /// /// ``` @@ -402,6 +404,8 @@ macro_rules! int_impl { /// /// On little endian this is a no-op. On big endian the bytes are swapped. /// + /// See also [from_le_bytes()](Self::from_le_bytes). + /// /// # Examples /// /// ``` @@ -428,9 +432,15 @@ macro_rules! int_impl { } } - /// Converts `self` to big endian from the target's endianness. + /// Swaps bytes of `self` on little endian targets. /// - /// On big endian this is a no-op. On little endian the bytes are swapped. + /// On big endian this is a no-op. + /// + /// The returned value has the same type as `self`, and will be interpreted + /// as (a potentially different) value of a native-endian + #[doc = concat!("`", stringify!($SelfT), "`.")] + /// + /// See [`to_be_bytes()`](Self::to_be_bytes) for a type-safe alternative. /// /// # Examples /// @@ -459,9 +469,15 @@ macro_rules! int_impl { } } - /// Converts `self` to little endian from the target's endianness. + /// Swaps bytes of `self` on big endian targets. /// - /// On little endian this is a no-op. On big endian the bytes are swapped. + /// On little endian this is a no-op. + /// + /// The returned value has the same type as `self`, and will be interpreted + /// as (a potentially different) value of a native-endian + #[doc = concat!("`", stringify!($SelfT), "`.")] + /// + /// See [`to_le_bytes()`](Self::to_le_bytes) for a type-safe alternative. /// /// # Examples /// diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 1d108cb0cf4a9..e3e843f955bd2 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -479,6 +479,76 @@ macro_rules! uint_impl { intrinsics::bswap(self as $ActualT) as Self } + /// Returns an integer with the bit locations specified by `mask` packed + /// contiguously into the least significant bits of the result. + /// ``` + /// #![feature(uint_gather_scatter_bits)] + #[doc = concat!("let n: ", stringify!($SelfT), " = 0b1011_1100;")] + /// + /// assert_eq!(n.gather_bits(0b0010_0100), 0b0000_0011); + /// assert_eq!(n.gather_bits(0xF0), 0b0000_1011); + /// ``` + #[unstable(feature = "uint_gather_scatter_bits", issue = "149069")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn gather_bits(self, mut mask: Self) -> Self { + let mut bit_position = 1; + let mut result = 0; + + // Iterate through the mask bits, unsetting the lowest bit after + // each iteration. We fill the bits in the result starting from the + // least significant bit. + while mask != 0 { + // Find the next lowest set bit in the mask + let next_mask_bit = mask.isolate_lowest_one(); + + // Retrieve the masked bit and if present, set it in the result + let src_bit = (self & next_mask_bit) != 0; + result |= if src_bit { bit_position } else { 0 }; + + // Unset lowest set bit in the mask, prepare next position to set + mask ^= next_mask_bit; + bit_position <<= 1; + } + + result + } + + /// Returns an integer with the least significant bits of `self` + /// distributed to the bit locations specified by `mask`. + /// ``` + /// #![feature(uint_gather_scatter_bits)] + #[doc = concat!("let n: ", stringify!($SelfT), " = 0b1010_1101;")] + /// + /// assert_eq!(n.scatter_bits(0b0101_0101), 0b0101_0001); + /// assert_eq!(n.scatter_bits(0xF0), 0b1101_0000); + /// ``` + #[unstable(feature = "uint_gather_scatter_bits", issue = "149069")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn scatter_bits(mut self, mut mask: Self) -> Self { + let mut result = 0; + + // Iterate through the mask bits, unsetting the lowest bit after + // each iteration and right-shifting `self` by one to get the next + // bit into the least significant bit position. + while mask != 0 { + // Find the next bit position to potentially set + let next_mask_bit = mask.isolate_lowest_one(); + + // If bit is set, deposit it at the masked bit position + result |= if (self & 1) != 0 { next_mask_bit } else { 0 }; + + // Unset lowest set bit in the mask, shift in next `self` bit + mask ^= next_mask_bit; + self >>= 1; + } + + result + } + /// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, /// second least-significant bit becomes second most-significant bit, etc. /// diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 84a6982d56805..2860bf0a6e69a 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1567,6 +1567,10 @@ impl *const [T; N] { /// Pointer equality is by address, as produced by the [`<*const T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialEq for *const T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] @@ -1577,10 +1581,18 @@ impl PartialEq for *const T { /// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Eq for *const T {} /// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Ord for *const T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] @@ -1597,6 +1609,10 @@ impl Ord for *const T { /// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialOrd for *const T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index ea0514f405f1e..29fe77390a16b 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -2520,6 +2520,10 @@ pub fn hash(hashee: *const T, into: &mut S) { } #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialEq for F { #[inline] fn eq(&self, other: &Self) -> bool { @@ -2527,9 +2531,17 @@ impl PartialEq for F { } } #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Eq for F {} #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialOrd for F { #[inline] fn partial_cmp(&self, other: &Self) -> Option { @@ -2537,6 +2549,10 @@ impl PartialOrd for F { } } #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Ord for F { #[inline] fn cmp(&self, other: &Self) -> Ordering { diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 85d54b4d3b9b3..f8dc6ef7ed71f 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -2000,6 +2000,10 @@ impl *mut [T; N] { /// Pointer equality is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialEq for *mut T { #[inline(always)] #[allow(ambiguous_wide_pointer_comparisons)] @@ -2010,10 +2014,18 @@ impl PartialEq for *mut T { /// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Eq for *mut T {} /// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Ord for *mut T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] @@ -2030,6 +2042,10 @@ impl Ord for *mut T { /// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialOrd for *mut T { #[inline(always)] #[allow(ambiguous_wide_pointer_comparisons)] diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 7053ae86e732f..a2fbf6ead6461 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -139,7 +139,7 @@ impl<'a, T> Iter<'a, T> { } } -iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, as_ref, { +iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, as_ref, each_ref, { fn is_sorted_by(self, mut compare: F) -> bool where Self: Sized, @@ -368,7 +368,7 @@ impl AsRef<[T]> for IterMut<'_, T> { // } // } -iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, as_mut, {}} +iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, as_mut, each_mut, {}} /// An internal abstraction over the splitting iterators, so that /// splitn, splitn_mut etc can be implemented once. diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index c46b7c797aab6..236bdf9d89cae 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -68,6 +68,7 @@ macro_rules! iterator { $raw_mut:tt, {$( $mut_:tt )?}, $into_ref:ident, + $array_ref:ident, {$($extra:tt)*} ) => { impl<'a, T> $name<'a, T> { @@ -190,6 +191,29 @@ macro_rules! iterator { } } + fn next_chunk(&mut self) -> Result<[$elem; N], crate::array::IntoIter<$elem, N>> { + if T::IS_ZST { + return crate::array::iter_next_chunk(self); + } + let len = len!(self); + if len >= N { + // SAFETY: we are just getting an array of [T; N] and moving the pointer over a little + let r = unsafe { self.post_inc_start(N).cast_array().$into_ref() } + .$array_ref(); // must convert &[T; N] to [&T; N] + Ok(r) + } else { + // cant use $array_ref because theres no builtin for &mut [MU; N] -> [&mut MU; N] + // cant use copy_nonoverlapping as the $elem is of type &{mut} T instead of T + let mut a = [const { crate::mem::MaybeUninit::<$elem>::uninit() }; N]; + for into in (&mut a).into_iter().take(len) { + // SAFETY: take(n) limits to remainder (slice produces worse codegen) + into.write(unsafe { self.post_inc_start(1).$into_ref() }); + } + // SAFETY: we just initialized elements 0..len + unsafe { Err(crate::array::IntoIter::new_unchecked(a, 0..len)) } + } + } + #[inline] fn size_hint(&self) -> (usize, Option) { let exact = len!(self); diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index e190536abcf9f..48982d71e8137 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -81,7 +81,6 @@ #![feature(lazy_get)] #![feature(maybe_uninit_fill)] #![feature(maybe_uninit_uninit_array_transpose)] -#![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(never_type)] #![feature(new_range_api)] @@ -115,6 +114,7 @@ #![feature(try_find)] #![feature(try_trait_v2)] #![feature(uint_bit_width)] +#![feature(uint_gather_scatter_bits)] #![feature(unsize)] #![feature(unwrap_infallible)] // tidy-alphabetical-end diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index b89a371efcc25..7f3e27e9c446c 100644 --- a/library/coretests/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -127,6 +127,52 @@ macro_rules! uint_module { assert_eq_const_safe!($T: _1.swap_bytes(), _1); } + fn test_gather_bits() { + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0011), 0b_0001); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0110), 0b_0010); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_1100), 0b_0001); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0001_1000), 0b_0000); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0011_0000), 0b_0010); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0110_0000), 0b_0001); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b1100_0000), 0b_0010); + + assert_eq_const_safe!($T: A.gather_bits(_0), 0); + assert_eq_const_safe!($T: B.gather_bits(_0), 0); + assert_eq_const_safe!($T: C.gather_bits(_0), 0); + assert_eq_const_safe!($T: _0.gather_bits(A), 0); + assert_eq_const_safe!($T: _0.gather_bits(B), 0); + assert_eq_const_safe!($T: _0.gather_bits(C), 0); + + assert_eq_const_safe!($T: A.gather_bits(_1), A); + assert_eq_const_safe!($T: B.gather_bits(_1), B); + assert_eq_const_safe!($T: C.gather_bits(_1), C); + assert_eq_const_safe!($T: _1.gather_bits(0b0010_0001), 0b0000_0011); + assert_eq_const_safe!($T: _1.gather_bits(0b0010_1100), 0b0000_0111); + assert_eq_const_safe!($T: _1.gather_bits(0b0111_1001), 0b0001_1111); + } + + fn test_scatter_bits() { + assert_eq_const_safe!($T: $T::scatter_bits(0b1111, 0b1001_0110), 0b1001_0110); + assert_eq_const_safe!($T: $T::scatter_bits(0b0001, 0b1001_0110), 0b0000_0010); + assert_eq_const_safe!($T: $T::scatter_bits(0b0010, 0b1001_0110), 0b0000_0100); + assert_eq_const_safe!($T: $T::scatter_bits(0b0100, 0b1001_0110), 0b0001_0000); + assert_eq_const_safe!($T: $T::scatter_bits(0b1000, 0b1001_0110), 0b1000_0000); + + assert_eq_const_safe!($T: A.scatter_bits(_0), 0); + assert_eq_const_safe!($T: B.scatter_bits(_0), 0); + assert_eq_const_safe!($T: C.scatter_bits(_0), 0); + assert_eq_const_safe!($T: _0.scatter_bits(A), 0); + assert_eq_const_safe!($T: _0.scatter_bits(B), 0); + assert_eq_const_safe!($T: _0.scatter_bits(C), 0); + + assert_eq_const_safe!($T: A.scatter_bits(_1), A); + assert_eq_const_safe!($T: B.scatter_bits(_1), B); + assert_eq_const_safe!($T: C.scatter_bits(_1), C); + assert_eq_const_safe!($T: _1.scatter_bits(A), A); + assert_eq_const_safe!($T: _1.scatter_bits(B), B); + assert_eq_const_safe!($T: _1.scatter_bits(C), C); + } + fn test_reverse_bits() { assert_eq_const_safe!($T: A.reverse_bits().reverse_bits(), A); assert_eq_const_safe!($T: B.reverse_bits().reverse_bits(), B); diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml index 0042a6e8ece58..3a4840a57334b 100644 --- a/library/proc_macro/Cargo.toml +++ b/library/proc_macro/Cargo.toml @@ -14,3 +14,6 @@ rustc-literal-escaper = { version = "0.0.5", features = ["rustc-dep-of-std"] } [features] default = ["rustc-dep-of-std"] rustc-dep-of-std = [] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)'] } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 4efdfcad924b5..108ef7bce81a2 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -22,7 +22,7 @@ #![feature(staged_api)] #![feature(allow_internal_unstable)] #![feature(decl_macro)] -#![feature(maybe_uninit_write_slice)] +#![cfg_attr(bootstrap, feature(maybe_uninit_write_slice))] #![feature(negative_impls)] #![feature(panic_can_unwind)] #![feature(restricted_std)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 32f166e112e99..8df87124245bc 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -349,7 +349,6 @@ #![feature(lazy_get)] #![feature(maybe_uninit_array_assume_init)] #![feature(maybe_uninit_slice)] -#![feature(maybe_uninit_write_slice)] #![feature(panic_can_unwind)] #![feature(panic_internals)] #![feature(pin_coerce_unsized_trait)] diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 5c0ac526a36c9..dbcf2684c6fb1 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1206,6 +1206,30 @@ impl Command { pub fn get_current_dir(&self) -> Option<&Path> { self.inner.get_current_dir() } + + /// Returns whether the environment will be cleared for the child process. + /// + /// This returns `true` if [`Command::env_clear`] was called, and `false` otherwise. + /// When `true`, the child process will not inherit any environment variables from + /// its parent process. + /// + /// # Examples + /// + /// ``` + /// #![feature(command_resolved_envs)] + /// use std::process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// assert_eq!(cmd.get_env_clear(), false); + /// + /// cmd.env_clear(); + /// assert_eq!(cmd.get_env_clear(), true); + /// ``` + #[must_use] + #[unstable(feature = "command_resolved_envs", issue = "149070")] + pub fn get_env_clear(&self) -> bool { + self.inner.get_env_clear() + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/sys/pal/motor/mod.rs b/library/std/src/sys/pal/motor/mod.rs index c64f8ff7a8a83..32f95df6ad082 100644 --- a/library/std/src/sys/pal/motor/mod.rs +++ b/library/std/src/sys/pal/motor/mod.rs @@ -58,16 +58,27 @@ pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { } match code as moto_rt::ErrorCode /* u16 */ { - E_ALREADY_IN_USE => ErrorKind::AlreadyExists, - E_INVALID_FILENAME => ErrorKind::InvalidFilename, + E_UNSPECIFIED => ErrorKind::Uncategorized, + E_UNKNOWN => ErrorKind::Uncategorized, + E_NOT_READY => ErrorKind::WouldBlock, + E_NOT_IMPLEMENTED => ErrorKind::Unsupported, + E_VERSION_TOO_HIGH => ErrorKind::Unsupported, + E_VERSION_TOO_LOW => ErrorKind::Unsupported, + E_INVALID_ARGUMENT => ErrorKind::InvalidInput, + E_OUT_OF_MEMORY => ErrorKind::OutOfMemory, + E_NOT_ALLOWED => ErrorKind::PermissionDenied, E_NOT_FOUND => ErrorKind::NotFound, + E_INTERNAL_ERROR => ErrorKind::Other, E_TIMED_OUT => ErrorKind::TimedOut, - E_NOT_IMPLEMENTED => ErrorKind::Unsupported, - E_FILE_TOO_LARGE => ErrorKind::FileTooLarge, + E_ALREADY_IN_USE => ErrorKind::AlreadyExists, E_UNEXPECTED_EOF => ErrorKind::UnexpectedEof, - E_INVALID_ARGUMENT => ErrorKind::InvalidInput, - E_NOT_READY => ErrorKind::WouldBlock, + E_INVALID_FILENAME => ErrorKind::InvalidFilename, + E_NOT_A_DIRECTORY => ErrorKind::NotADirectory, + E_BAD_HANDLE => ErrorKind::InvalidInput, + E_FILE_TOO_LARGE => ErrorKind::FileTooLarge, E_NOT_CONNECTED => ErrorKind::NotConnected, + E_STORAGE_FULL => ErrorKind::StorageFull, + E_INVALID_DATA => ErrorKind::InvalidData, _ => crate::io::ErrorKind::Uncategorized, } } diff --git a/library/std/src/sys/process/motor.rs b/library/std/src/sys/process/motor.rs index 9060902bc3d20..949a9d4942901 100644 --- a/library/std/src/sys/process/motor.rs +++ b/library/std/src/sys/process/motor.rs @@ -98,6 +98,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { self.cwd.as_ref().map(Path::new) } diff --git a/library/std/src/sys/process/uefi.rs b/library/std/src/sys/process/uefi.rs index 11c8b682bb9bc..8d44292611bcb 100644 --- a/library/std/src/sys/process/uefi.rs +++ b/library/std/src/sys/process/uefi.rs @@ -83,6 +83,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { None } diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs index 1d5909e99bacc..44d54aaf51512 100644 --- a/library/std/src/sys/process/unix/common.rs +++ b/library/std/src/sys/process/unix/common.rs @@ -263,6 +263,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes()))) } diff --git a/library/std/src/sys/process/unsupported.rs b/library/std/src/sys/process/unsupported.rs index 636465b68e541..2dfc676ec0059 100644 --- a/library/std/src/sys/process/unsupported.rs +++ b/library/std/src/sys/process/unsupported.rs @@ -86,6 +86,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { self.cwd.as_ref().map(|cs| Path::new(cs)) } diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index 7d58093c54bbf..6e8be21a1fa6d 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -250,6 +250,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { self.cwd.as_ref().map(Path::new) } diff --git a/library/std/src/thread/builder.rs b/library/std/src/thread/builder.rs new file mode 100644 index 0000000000000..f4abe074ab9d7 --- /dev/null +++ b/library/std/src/thread/builder.rs @@ -0,0 +1,267 @@ +use super::join_handle::JoinHandle; +use super::lifecycle::spawn_unchecked; +use crate::io; + +/// Thread factory, which can be used in order to configure the properties of +/// a new thread. +/// +/// Methods can be chained on it in order to configure it. +/// +/// The two configurations available are: +/// +/// - [`name`]: specifies an [associated name for the thread][naming-threads] +/// - [`stack_size`]: specifies the [desired stack size for the thread][stack-size] +/// +/// The [`spawn`] method will take ownership of the builder and create an +/// [`io::Result`] to the thread handle with the given configuration. +/// +/// The [`thread::spawn`] free function uses a `Builder` with default +/// configuration and [`unwrap`]s its return value. +/// +/// You may want to use [`spawn`] instead of [`thread::spawn`], when you want +/// to recover from a failure to launch a thread, indeed the free function will +/// panic where the `Builder` method will return a [`io::Result`]. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// let builder = thread::Builder::new(); +/// +/// let handler = builder.spawn(|| { +/// // thread code +/// }).unwrap(); +/// +/// handler.join().unwrap(); +/// ``` +/// +/// [`stack_size`]: Builder::stack_size +/// [`name`]: Builder::name +/// [`spawn`]: Builder::spawn +/// [`thread::spawn`]: super::spawn +/// [`io::Result`]: crate::io::Result +/// [`unwrap`]: crate::result::Result::unwrap +/// [naming-threads]: ./index.html#naming-threads +/// [stack-size]: ./index.html#stack-size +#[must_use = "must eventually spawn the thread"] +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct Builder { + /// A name for the thread-to-be, for identification in panic messages + pub(super) name: Option, + /// The size of the stack for the spawned thread in bytes + pub(super) stack_size: Option, + /// Skip running and inheriting the thread spawn hooks + pub(super) no_hooks: bool, +} + +impl Builder { + /// Generates the base configuration for spawning a thread, from which + /// configuration methods can be chained. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()) + /// .stack_size(32 * 1024); + /// + /// let handler = builder.spawn(|| { + /// // thread code + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> Builder { + Builder { name: None, stack_size: None, no_hooks: false } + } + + /// Names the thread-to-be. Currently the name is used for identification + /// only in panic messages. + /// + /// The name must not contain null bytes (`\0`). + /// + /// For more information about named threads, see + /// [this module-level documentation][naming-threads]. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()); + /// + /// let handler = builder.spawn(|| { + /// assert_eq!(thread::current().name(), Some("foo")) + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// [naming-threads]: ./index.html#naming-threads + #[stable(feature = "rust1", since = "1.0.0")] + pub fn name(mut self, name: String) -> Builder { + self.name = Some(name); + self + } + + /// Sets the size of the stack (in bytes) for the new thread. + /// + /// The actual stack size may be greater than this value if + /// the platform specifies a minimal stack size. + /// + /// For more information about the stack size for threads, see + /// [this module-level documentation][stack-size]. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new().stack_size(32 * 1024); + /// ``` + /// + /// [stack-size]: ./index.html#stack-size + #[stable(feature = "rust1", since = "1.0.0")] + pub fn stack_size(mut self, size: usize) -> Builder { + self.stack_size = Some(size); + self + } + + /// Disables running and inheriting [spawn hooks]. + /// + /// Use this if the parent thread is in no way relevant for the child thread. + /// For example, when lazily spawning threads for a thread pool. + /// + /// [spawn hooks]: super::add_spawn_hook + #[unstable(feature = "thread_spawn_hook", issue = "132951")] + pub fn no_hooks(mut self) -> Builder { + self.no_hooks = true; + self + } + + /// Spawns a new thread by taking ownership of the `Builder`, and returns an + /// [`io::Result`] to its [`JoinHandle`]. + /// + /// The spawned thread may outlive the caller (unless the caller thread + /// is the main thread; the whole process is terminated when the main + /// thread finishes). The join handle can be used to block on + /// termination of the spawned thread, including recovering its panics. + /// + /// For a more complete documentation see [`thread::spawn`]. + /// + /// # Errors + /// + /// Unlike the [`spawn`] free function, this method yields an + /// [`io::Result`] to capture any failure to create the thread at + /// the OS level. + /// + /// [`io::Result`]: crate::io::Result + /// + /// # Panics + /// + /// Panics if a thread name was set and it contained null bytes. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let handler = builder.spawn(|| { + /// // thread code + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// [`thread::spawn`]: super::spawn + /// [`spawn`]: super::spawn + #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn spawn(self, f: F) -> io::Result> + where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, + { + unsafe { self.spawn_unchecked(f) } + } + + /// Spawns a new thread without any lifetime restrictions by taking ownership + /// of the `Builder`, and returns an [`io::Result`] to its [`JoinHandle`]. + /// + /// The spawned thread may outlive the caller (unless the caller thread + /// is the main thread; the whole process is terminated when the main + /// thread finishes). The join handle can be used to block on + /// termination of the spawned thread, including recovering its panics. + /// + /// This method is identical to [`thread::Builder::spawn`][`Builder::spawn`], + /// except for the relaxed lifetime bounds, which render it unsafe. + /// For a more complete documentation see [`thread::spawn`]. + /// + /// # Errors + /// + /// Unlike the [`spawn`] free function, this method yields an + /// [`io::Result`] to capture any failure to create the thread at + /// the OS level. + /// + /// # Panics + /// + /// Panics if a thread name was set and it contained null bytes. + /// + /// # Safety + /// + /// The caller has to ensure that the spawned thread does not outlive any + /// references in the supplied thread closure and its return type. + /// This can be guaranteed in two ways: + /// + /// - ensure that [`join`][`JoinHandle::join`] is called before any referenced + /// data is dropped + /// - use only types with `'static` lifetime bounds, i.e., those with no or only + /// `'static` references (both [`thread::Builder::spawn`][`Builder::spawn`] + /// and [`thread::spawn`] enforce this property statically) + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let x = 1; + /// let thread_x = &x; + /// + /// let handler = unsafe { + /// builder.spawn_unchecked(move || { + /// println!("x = {}", *thread_x); + /// }).unwrap() + /// }; + /// + /// // caller has to ensure `join()` is called, otherwise + /// // it is possible to access freed memory if `x` gets + /// // dropped before the thread closure is executed! + /// handler.join().unwrap(); + /// ``` + /// + /// [`io::Result`]: crate::io::Result + /// [`thread::spawn`]: super::spawn + /// [`spawn`]: super::spawn + #[stable(feature = "thread_spawn_unchecked", since = "1.82.0")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub unsafe fn spawn_unchecked(self, f: F) -> io::Result> + where + F: FnOnce() -> T, + F: Send, + T: Send, + { + let Builder { name, stack_size, no_hooks } = self; + Ok(JoinHandle(unsafe { spawn_unchecked(name, stack_size, no_hooks, None, f) }?)) + } +} diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs index f00212bfcb617..fab6b0cd9d6f2 100644 --- a/library/std/src/thread/current.rs +++ b/library/std/src/thread/current.rs @@ -1,6 +1,9 @@ -use super::{Thread, ThreadId, imp}; +use super::id::ThreadId; +use super::main_thread; +use super::thread::Thread; use crate::mem::ManuallyDrop; use crate::ptr; +use crate::sys::thread as imp; use crate::sys::thread_local::local_pointer; const NONE: *mut () = ptr::null_mut(); @@ -184,7 +187,7 @@ pub(crate) fn current_os_id() -> u64 { /// Gets a reference to the handle of the thread that invokes it, if the handle /// has been initialized. -pub(super) fn try_with_current(f: F) -> R +fn try_with_current(f: F) -> R where F: FnOnce(Option<&Thread>) -> R, { @@ -202,6 +205,36 @@ where } } +/// Run a function with the current thread's name. +/// +/// Modulo thread local accesses, this function is safe to call from signal +/// handlers and in similar circumstances where allocations are not possible. +pub(crate) fn with_current_name(f: F) -> R +where + F: FnOnce(Option<&str>) -> R, +{ + try_with_current(|thread| { + let name = if let Some(thread) = thread { + // If there is a current thread handle, try to use the name stored + // there. + thread.name() + } else if let Some(main) = main_thread::get() + && let Some(id) = id::get() + && id == main + { + // The main thread doesn't always have a thread handle, we must + // identify it through its ID instead. The checks are ordered so + // that the current ID is only loaded if it is actually needed, + // since loading it from TLS might need multiple expensive accesses. + Some("main") + } else { + None + }; + + f(name) + }) +} + /// Gets a handle to the thread that invokes it. If the handle stored in thread- /// local storage was already destroyed, this creates a new unnamed temporary /// handle to allow thread parking in nearly all situations. diff --git a/library/std/src/thread/functions.rs b/library/std/src/thread/functions.rs new file mode 100644 index 0000000000000..a25bae1aae31e --- /dev/null +++ b/library/std/src/thread/functions.rs @@ -0,0 +1,692 @@ +//! Free functions. + +use super::builder::Builder; +use super::current::current; +use super::join_handle::JoinHandle; +use crate::mem::forget; +use crate::num::NonZero; +use crate::sys::thread as imp; +use crate::time::{Duration, Instant}; +use crate::{io, panicking}; + +/// Spawns a new thread, returning a [`JoinHandle`] for it. +/// +/// The join handle provides a [`join`] method that can be used to join the spawned +/// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing +/// the argument given to [`panic!`]. +/// +/// If the join handle is dropped, the spawned thread will implicitly be *detached*. +/// In this case, the spawned thread may no longer be joined. +/// (It is the responsibility of the program to either eventually join threads it +/// creates or detach them; otherwise, a resource leak will result.) +/// +/// This function creates a thread with the default parameters of [`Builder`]. +/// To specify the new thread's stack size or the name, use [`Builder::spawn`]. +/// +/// As you can see in the signature of `spawn` there are two constraints on +/// both the closure given to `spawn` and its return value, let's explain them: +/// +/// - The `'static` constraint means that the closure and its return value +/// must have a lifetime of the whole program execution. The reason for this +/// is that threads can outlive the lifetime they have been created in. +/// +/// Indeed if the thread, and by extension its return value, can outlive their +/// caller, we need to make sure that they will be valid afterwards, and since +/// we *can't* know when it will return we need to have them valid as long as +/// possible, that is until the end of the program, hence the `'static` +/// lifetime. +/// - The [`Send`] constraint is because the closure will need to be passed +/// *by value* from the thread where it is spawned to the new thread. Its +/// return value will need to be passed from the new thread to the thread +/// where it is `join`ed. +/// As a reminder, the [`Send`] marker trait expresses that it is safe to be +/// passed from thread to thread. [`Sync`] expresses that it is safe to have a +/// reference be passed from thread to thread. +/// +/// # Panics +/// +/// Panics if the OS fails to create a thread; use [`Builder::spawn`] +/// to recover from such errors. +/// +/// # Examples +/// +/// Creating a thread. +/// +/// ``` +/// use std::thread; +/// +/// let handler = thread::spawn(|| { +/// // thread code +/// }); +/// +/// handler.join().unwrap(); +/// ``` +/// +/// As mentioned in the module documentation, threads are usually made to +/// communicate using [`channels`], here is how it usually looks. +/// +/// This example also shows how to use `move`, in order to give ownership +/// of values to a thread. +/// +/// ``` +/// use std::thread; +/// use std::sync::mpsc::channel; +/// +/// let (tx, rx) = channel(); +/// +/// let sender = thread::spawn(move || { +/// tx.send("Hello, thread".to_owned()) +/// .expect("Unable to send on channel"); +/// }); +/// +/// let receiver = thread::spawn(move || { +/// let value = rx.recv().expect("Unable to receive from channel"); +/// println!("{value}"); +/// }); +/// +/// sender.join().expect("The sender thread has panicked"); +/// receiver.join().expect("The receiver thread has panicked"); +/// ``` +/// +/// A thread can also return a value through its [`JoinHandle`], you can use +/// this to make asynchronous computations (futures might be more appropriate +/// though). +/// +/// ``` +/// use std::thread; +/// +/// let computation = thread::spawn(|| { +/// // Some expensive computation. +/// 42 +/// }); +/// +/// let result = computation.join().unwrap(); +/// println!("{result}"); +/// ``` +/// +/// # Notes +/// +/// This function has the same minimal guarantee regarding "foreign" unwinding operations (e.g. +/// an exception thrown from C++ code, or a `panic!` in Rust code compiled or linked with a +/// different runtime) as [`catch_unwind`]; namely, if the thread created with `thread::spawn` +/// unwinds all the way to the root with such an exception, one of two behaviors are possible, +/// and it is unspecified which will occur: +/// +/// * The process aborts. +/// * The process does not abort, and [`join`] will return a `Result::Err` +/// containing an opaque type. +/// +/// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html +/// [`channels`]: crate::sync::mpsc +/// [`join`]: JoinHandle::join +/// [`Err`]: crate::result::Result::Err +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +pub fn spawn(f: F) -> JoinHandle +where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, +{ + Builder::new().spawn(f).expect("failed to spawn thread") +} + +/// Cooperatively gives up a timeslice to the OS scheduler. +/// +/// This calls the underlying OS scheduler's yield primitive, signaling +/// that the calling thread is willing to give up its remaining timeslice +/// so that the OS may schedule other threads on the CPU. +/// +/// A drawback of yielding in a loop is that if the OS does not have any +/// other ready threads to run on the current CPU, the thread will effectively +/// busy-wait, which wastes CPU time and energy. +/// +/// Therefore, when waiting for events of interest, a programmer's first +/// choice should be to use synchronization devices such as [`channel`]s, +/// [`Condvar`]s, [`Mutex`]es or [`join`] since these primitives are +/// implemented in a blocking manner, giving up the CPU until the event +/// of interest has occurred which avoids repeated yielding. +/// +/// `yield_now` should thus be used only rarely, mostly in situations where +/// repeated polling is required because there is no other suitable way to +/// learn when an event of interest has occurred. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// thread::yield_now(); +/// ``` +/// +/// [`channel`]: crate::sync::mpsc +/// [`join`]: JoinHandle::join +/// [`Condvar`]: crate::sync::Condvar +/// [`Mutex`]: crate::sync::Mutex +#[stable(feature = "rust1", since = "1.0.0")] +pub fn yield_now() { + imp::yield_now() +} + +/// Determines whether the current thread is unwinding because of panic. +/// +/// A common use of this feature is to poison shared resources when writing +/// unsafe code, by checking `panicking` when the `drop` is called. +/// +/// This is usually not needed when writing safe code, as [`Mutex`es][Mutex] +/// already poison themselves when a thread panics while holding the lock. +/// +/// This can also be used in multithreaded applications, in order to send a +/// message to other threads warning that a thread has panicked (e.g., for +/// monitoring purposes). +/// +/// # Examples +/// +/// ```should_panic +/// use std::thread; +/// +/// struct SomeStruct; +/// +/// impl Drop for SomeStruct { +/// fn drop(&mut self) { +/// if thread::panicking() { +/// println!("dropped while unwinding"); +/// } else { +/// println!("dropped while not unwinding"); +/// } +/// } +/// } +/// +/// { +/// print!("a: "); +/// let a = SomeStruct; +/// } +/// +/// { +/// print!("b: "); +/// let b = SomeStruct; +/// panic!() +/// } +/// ``` +/// +/// [Mutex]: crate::sync::Mutex +#[inline] +#[must_use] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn panicking() -> bool { + panicking::panicking() +} + +/// Uses [`sleep`]. +/// +/// Puts the current thread to sleep for at least the specified amount of time. +/// +/// The thread may sleep longer than the duration specified due to scheduling +/// specifics or platform-dependent functionality. It will never sleep less. +/// +/// This function is blocking, and should not be used in `async` functions. +/// +/// # Platform-specific behavior +/// +/// On Unix platforms, the underlying syscall may be interrupted by a +/// spurious wakeup or signal handler. To ensure the sleep occurs for at least +/// the specified duration, this function may invoke that system call multiple +/// times. +/// +/// # Examples +/// +/// ```no_run +/// use std::thread; +/// +/// // Let's sleep for 2 seconds: +/// thread::sleep_ms(2000); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[deprecated(since = "1.6.0", note = "replaced by `std::thread::sleep`")] +pub fn sleep_ms(ms: u32) { + sleep(Duration::from_millis(ms as u64)) +} + +/// Puts the current thread to sleep for at least the specified amount of time. +/// +/// The thread may sleep longer than the duration specified due to scheduling +/// specifics or platform-dependent functionality. It will never sleep less. +/// +/// This function is blocking, and should not be used in `async` functions. +/// +/// # Platform-specific behavior +/// +/// On Unix platforms, the underlying syscall may be interrupted by a +/// spurious wakeup or signal handler. To ensure the sleep occurs for at least +/// the specified duration, this function may invoke that system call multiple +/// times. +/// Platforms which do not support nanosecond precision for sleeping will +/// have `dur` rounded up to the nearest granularity of time they can sleep for. +/// +/// Currently, specifying a zero duration on Unix platforms returns immediately +/// without invoking the underlying [`nanosleep`] syscall, whereas on Windows +/// platforms the underlying [`Sleep`] syscall is always invoked. +/// If the intention is to yield the current time-slice you may want to use +/// [`yield_now`] instead. +/// +/// [`nanosleep`]: https://linux.die.net/man/2/nanosleep +/// [`Sleep`]: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep +/// +/// # Examples +/// +/// ```no_run +/// use std::{thread, time}; +/// +/// let ten_millis = time::Duration::from_millis(10); +/// let now = time::Instant::now(); +/// +/// thread::sleep(ten_millis); +/// +/// assert!(now.elapsed() >= ten_millis); +/// ``` +#[stable(feature = "thread_sleep", since = "1.4.0")] +pub fn sleep(dur: Duration) { + imp::sleep(dur) +} + +/// Puts the current thread to sleep until the specified deadline has passed. +/// +/// The thread may still be asleep after the deadline specified due to +/// scheduling specifics or platform-dependent functionality. It will never +/// wake before. +/// +/// This function is blocking, and should not be used in `async` functions. +/// +/// # Platform-specific behavior +/// +/// In most cases this function will call an OS specific function. Where that +/// is not supported [`sleep`] is used. Those platforms are referred to as other +/// in the table below. +/// +/// # Underlying System calls +/// +/// The following system calls are [currently] being used: +/// +/// | Platform | System call | +/// |-----------|----------------------------------------------------------------------| +/// | Linux | [clock_nanosleep] (Monotonic clock) | +/// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock)] | +/// | Android | [clock_nanosleep] (Monotonic Clock)] | +/// | Solaris | [clock_nanosleep] (Monotonic Clock)] | +/// | Illumos | [clock_nanosleep] (Monotonic Clock)] | +/// | Dragonfly | [clock_nanosleep] (Monotonic Clock)] | +/// | Hurd | [clock_nanosleep] (Monotonic Clock)] | +/// | Fuchsia | [clock_nanosleep] (Monotonic Clock)] | +/// | Vxworks | [clock_nanosleep] (Monotonic Clock)] | +/// | Other | `sleep_until` uses [`sleep`] and does not issue a syscall itself | +/// +/// [currently]: crate::io#platform-specific-behavior +/// [clock_nanosleep]: https://linux.die.net/man/3/clock_nanosleep +/// +/// **Disclaimer:** These system calls might change over time. +/// +/// # Examples +/// +/// A simple game loop that limits the game to 60 frames per second. +/// +/// ```no_run +/// #![feature(thread_sleep_until)] +/// # use std::time::{Duration, Instant}; +/// # use std::thread; +/// # +/// # fn update() {} +/// # fn render() {} +/// # +/// let max_fps = 60.0; +/// let frame_time = Duration::from_secs_f32(1.0/max_fps); +/// let mut next_frame = Instant::now(); +/// loop { +/// thread::sleep_until(next_frame); +/// next_frame += frame_time; +/// update(); +/// render(); +/// } +/// ``` +/// +/// A slow API we must not call too fast and which takes a few +/// tries before succeeding. By using `sleep_until` the time the +/// API call takes does not influence when we retry or when we give up +/// +/// ```no_run +/// #![feature(thread_sleep_until)] +/// # use std::time::{Duration, Instant}; +/// # use std::thread; +/// # +/// # enum Status { +/// # Ready(usize), +/// # Waiting, +/// # } +/// # fn slow_web_api_call() -> Status { Status::Ready(42) } +/// # +/// # const MAX_DURATION: Duration = Duration::from_secs(10); +/// # +/// # fn try_api_call() -> Result { +/// let deadline = Instant::now() + MAX_DURATION; +/// let delay = Duration::from_millis(250); +/// let mut next_attempt = Instant::now(); +/// loop { +/// if Instant::now() > deadline { +/// break Err(()); +/// } +/// if let Status::Ready(data) = slow_web_api_call() { +/// break Ok(data); +/// } +/// +/// next_attempt = deadline.min(next_attempt + delay); +/// thread::sleep_until(next_attempt); +/// } +/// # } +/// # let _data = try_api_call(); +/// ``` +#[unstable(feature = "thread_sleep_until", issue = "113752")] +pub fn sleep_until(deadline: Instant) { + imp::sleep_until(deadline) +} + +/// Used to ensure that `park` and `park_timeout` do not unwind, as that can +/// cause undefined behavior if not handled correctly (see #102398 for context). +struct PanicGuard; + +impl Drop for PanicGuard { + fn drop(&mut self) { + rtabort!("an irrecoverable error occurred while synchronizing threads") + } +} + +/// Blocks unless or until the current thread's token is made available. +/// +/// A call to `park` does not guarantee that the thread will remain parked +/// forever, and callers should be prepared for this possibility. However, +/// it is guaranteed that this function will not panic (it may abort the +/// process if the implementation encounters some rare errors). +/// +/// # `park` and `unpark` +/// +/// Every thread is equipped with some basic low-level blocking support, via the +/// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`] +/// method. [`park`] blocks the current thread, which can then be resumed from +/// another thread by calling the [`unpark`] method on the blocked thread's +/// handle. +/// +/// Conceptually, each [`Thread`] handle has an associated token, which is +/// initially not present: +/// +/// * The [`thread::park`][`park`] function blocks the current thread unless or +/// until the token is available for its thread handle, at which point it +/// atomically consumes the token. It may also return *spuriously*, without +/// consuming the token. [`thread::park_timeout`] does the same, but allows +/// specifying a maximum time to block the thread for. +/// +/// * The [`unpark`] method on a [`Thread`] atomically makes the token available +/// if it wasn't already. Because the token can be held by a thread even if it is currently not +/// parked, [`unpark`] followed by [`park`] will result in the second call returning immediately. +/// However, note that to rely on this guarantee, you need to make sure that your `unpark` happens +/// after all `park` that may be done by other data structures! +/// +/// The API is typically used by acquiring a handle to the current thread, placing that handle in a +/// shared data structure so that other threads can find it, and then `park`ing in a loop. When some +/// desired condition is met, another thread calls [`unpark`] on the handle. The last bullet point +/// above guarantees that even if the `unpark` occurs before the thread is finished `park`ing, it +/// will be woken up properly. +/// +/// Note that the coordination via the shared data structure is crucial: If you `unpark` a thread +/// without first establishing that it is about to be `park`ing within your code, that `unpark` may +/// get consumed by a *different* `park` in the same thread, leading to a deadlock. This also means +/// you must not call unknown code between setting up for parking and calling `park`; for instance, +/// if you invoke `println!`, that may itself call `park` and thus consume your `unpark` and cause a +/// deadlock. +/// +/// The motivation for this design is twofold: +/// +/// * It avoids the need to allocate mutexes and condvars when building new +/// synchronization primitives; the threads already provide basic +/// blocking/signaling. +/// +/// * It can be implemented very efficiently on many platforms. +/// +/// # Memory Ordering +/// +/// Calls to `unpark` _synchronize-with_ calls to `park`, meaning that memory +/// operations performed before a call to `unpark` are made visible to the thread that +/// consumes the token and returns from `park`. Note that all `park` and `unpark` +/// operations for a given thread form a total order and _all_ prior `unpark` operations +/// synchronize-with `park`. +/// +/// In atomic ordering terms, `unpark` performs a `Release` operation and `park` +/// performs the corresponding `Acquire` operation. Calls to `unpark` for the same +/// thread form a [release sequence]. +/// +/// Note that being unblocked does not imply a call was made to `unpark`, because +/// wakeups can also be spurious. For example, a valid, but inefficient, +/// implementation could have `park` and `unpark` return immediately without doing anything, +/// making *all* wakeups spurious. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// use std::sync::atomic::{Ordering, AtomicBool}; +/// use std::time::Duration; +/// +/// static QUEUED: AtomicBool = AtomicBool::new(false); +/// static FLAG: AtomicBool = AtomicBool::new(false); +/// +/// let parked_thread = thread::spawn(move || { +/// println!("Thread spawned"); +/// // Signal that we are going to `park`. Between this store and our `park`, there may +/// // be no other `park`, or else that `park` could consume our `unpark` token! +/// QUEUED.store(true, Ordering::Release); +/// // We want to wait until the flag is set. We *could* just spin, but using +/// // park/unpark is more efficient. +/// while !FLAG.load(Ordering::Acquire) { +/// // We can *not* use `println!` here since that could use thread parking internally. +/// thread::park(); +/// // We *could* get here spuriously, i.e., way before the 10ms below are over! +/// // But that is no problem, we are in a loop until the flag is set anyway. +/// } +/// println!("Flag received"); +/// }); +/// +/// // Let some time pass for the thread to be spawned. +/// thread::sleep(Duration::from_millis(10)); +/// +/// // Ensure the thread is about to park. +/// // This is crucial! It guarantees that the `unpark` below is not consumed +/// // by some other code in the parked thread (e.g. inside `println!`). +/// while !QUEUED.load(Ordering::Acquire) { +/// // Spinning is of course inefficient; in practice, this would more likely be +/// // a dequeue where we have no work to do if there's nobody queued. +/// std::hint::spin_loop(); +/// } +/// +/// // Set the flag, and let the thread wake up. +/// // There is no race condition here: if `unpark` +/// // happens first, `park` will return immediately. +/// // There is also no other `park` that could consume this token, +/// // since we waited until the other thread got queued. +/// // Hence there is no risk of a deadlock. +/// FLAG.store(true, Ordering::Release); +/// println!("Unpark the thread"); +/// parked_thread.thread().unpark(); +/// +/// parked_thread.join().unwrap(); +/// ``` +/// +/// [`Thread`]: super::Thread +/// [`unpark`]: super::Thread::unpark +/// [`thread::park_timeout`]: park_timeout +/// [release sequence]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release_sequence +#[stable(feature = "rust1", since = "1.0.0")] +pub fn park() { + let guard = PanicGuard; + // SAFETY: park_timeout is called on the parker owned by this thread. + unsafe { + current().park(); + } + // No panic occurred, do not abort. + forget(guard); +} + +/// Uses [`park_timeout`]. +/// +/// Blocks unless or until the current thread's token is made available or +/// the specified duration has been reached (may wake spuriously). +/// +/// The semantics of this function are equivalent to [`park`] 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 `ms` long. +/// +/// See the [park documentation][`park`] for more detail. +#[stable(feature = "rust1", since = "1.0.0")] +#[deprecated(since = "1.6.0", note = "replaced by `std::thread::park_timeout`")] +pub fn park_timeout_ms(ms: u32) { + park_timeout(Duration::from_millis(ms as u64)) +} + +/// Blocks unless or until the current thread's token is made available or +/// the specified duration has been reached (may wake spuriously). +/// +/// The semantics of this function are equivalent to [`park`][park] 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` long. +/// +/// See the [park documentation][park] for more details. +/// +/// # Platform-specific behavior +/// +/// Platforms which do not support nanosecond precision for sleeping will have +/// `dur` rounded up to the nearest granularity of time they can sleep for. +/// +/// # Examples +/// +/// Waiting for the complete expiration of the timeout: +/// +/// ```rust,no_run +/// use std::thread::park_timeout; +/// use std::time::{Instant, Duration}; +/// +/// let timeout = Duration::from_secs(2); +/// let beginning_park = Instant::now(); +/// +/// let mut timeout_remaining = timeout; +/// loop { +/// park_timeout(timeout_remaining); +/// let elapsed = beginning_park.elapsed(); +/// if elapsed >= timeout { +/// break; +/// } +/// println!("restarting park_timeout after {elapsed:?}"); +/// timeout_remaining = timeout - elapsed; +/// } +/// ``` +#[stable(feature = "park_timeout", since = "1.4.0")] +pub fn park_timeout(dur: Duration) { + let guard = PanicGuard; + // SAFETY: park_timeout is called on a handle owned by this thread. + unsafe { + current().park_timeout(dur); + } + // No panic occurred, do not abort. + forget(guard); +} + +/// Returns an estimate of the default amount of parallelism a program should use. +/// +/// Parallelism is a resource. A given machine provides a certain capacity for +/// parallelism, i.e., a bound on the number of computations it can perform +/// simultaneously. This number often corresponds to the amount of CPUs a +/// computer has, but it may diverge in various cases. +/// +/// Host environments such as VMs or container orchestrators may want to +/// restrict the amount of parallelism made available to programs in them. This +/// is often done to limit the potential impact of (unintentionally) +/// resource-intensive programs on other programs running on the same machine. +/// +/// # Limitations +/// +/// The purpose of this API is to provide an easy and portable way to query +/// the default amount of parallelism the program should use. Among other things it +/// does not expose information on NUMA regions, does not account for +/// differences in (co)processor capabilities or current system load, +/// and will not modify the program's global state in order to more accurately +/// query the amount of available parallelism. +/// +/// Where both fixed steady-state and burst limits are available the steady-state +/// capacity will be used to ensure more predictable latencies. +/// +/// Resource limits can be changed during the runtime of a program, therefore the value is +/// not cached and instead recomputed every time this function is called. It should not be +/// called from hot code. +/// +/// The value returned by this function should be considered a simplified +/// approximation of the actual amount of parallelism available at any given +/// time. To get a more detailed or precise overview of the amount of +/// parallelism available to the program, you may wish to use +/// platform-specific APIs as well. The following platform limitations currently +/// apply to `available_parallelism`: +/// +/// On Windows: +/// - It may undercount the amount of parallelism available on systems with more +/// than 64 logical CPUs. However, programs typically need specific support to +/// take advantage of more than 64 logical CPUs, and in the absence of such +/// support, the number returned by this function accurately reflects the +/// number of logical CPUs the program can use by default. +/// - It may overcount the amount of parallelism available on systems limited by +/// process-wide affinity masks, or job object limitations. +/// +/// On Linux: +/// - It may overcount the amount of parallelism available when limited by a +/// process-wide affinity mask or cgroup quotas and `sched_getaffinity()` or cgroup fs can't be +/// queried, e.g. due to sandboxing. +/// - It may undercount the amount of parallelism if the current thread's affinity mask +/// does not reflect the process' cpuset, e.g. due to pinned threads. +/// - If the process is in a cgroup v1 cpu controller, this may need to +/// scan mountpoints to find the corresponding cgroup v1 controller, +/// which may take time on systems with large numbers of mountpoints. +/// (This does not apply to cgroup v2, or to processes not in a +/// cgroup.) +/// - It does not attempt to take `ulimit` into account. If there is a limit set on the number of +/// threads, `available_parallelism` cannot know how much of that limit a Rust program should +/// take, or know in a reliable and race-free way how much of that limit is already taken. +/// +/// On all targets: +/// - It may overcount the amount of parallelism available when running in a VM +/// with CPU usage limits (e.g. an overcommitted host). +/// +/// # Errors +/// +/// This function will, but is not limited to, return errors in the following +/// cases: +/// +/// - If the amount of parallelism is not known for the target platform. +/// - If the program lacks permission to query the amount of parallelism made +/// available to it. +/// +/// # Examples +/// +/// ``` +/// # #![allow(dead_code)] +/// use std::{io, thread}; +/// +/// fn main() -> io::Result<()> { +/// let count = thread::available_parallelism()?.get(); +/// assert!(count >= 1_usize); +/// Ok(()) +/// } +/// ``` +#[doc(alias = "available_concurrency")] // Alias for a previous name we gave this API on unstable. +#[doc(alias = "hardware_concurrency")] // Alias for C++ `std::thread::hardware_concurrency`. +#[doc(alias = "num_cpus")] // Alias for a popular ecosystem crate which provides similar functionality. +#[stable(feature = "available_parallelism", since = "1.59.0")] +pub fn available_parallelism() -> io::Result> { + imp::available_parallelism() +} diff --git a/library/std/src/thread/id.rs b/library/std/src/thread/id.rs new file mode 100644 index 0000000000000..5ea5147681ce9 --- /dev/null +++ b/library/std/src/thread/id.rs @@ -0,0 +1,96 @@ +use crate::num::NonZero; + +/// A unique identifier for a running thread. +/// +/// A `ThreadId` is an opaque object that uniquely identifies each thread +/// created during the lifetime of a process. `ThreadId`s are guaranteed not to +/// be reused, even when a thread terminates. `ThreadId`s are under the control +/// of Rust's standard library and there may not be any relationship between +/// `ThreadId` and the underlying platform's notion of a thread identifier -- +/// the two concepts cannot, therefore, be used interchangeably. A `ThreadId` +/// can be retrieved from the [`id`] method on a [`Thread`]. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// let other_thread = thread::spawn(|| { +/// thread::current().id() +/// }); +/// +/// let other_thread_id = other_thread.join().unwrap(); +/// assert!(thread::current().id() != other_thread_id); +/// ``` +/// +/// [`Thread`]: super::Thread +/// [`id`]: super::Thread::id +#[stable(feature = "thread_id", since = "1.19.0")] +#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] +pub struct ThreadId(NonZero); + +impl ThreadId { + // Generate a new unique thread ID. + pub(crate) fn new() -> ThreadId { + #[cold] + fn exhausted() -> ! { + panic!("failed to generate unique thread ID: bitspace exhausted") + } + + cfg_select! { + target_has_atomic = "64" => { + use crate::sync::atomic::{Atomic, AtomicU64, Ordering}; + + static COUNTER: Atomic = AtomicU64::new(0); + + let mut last = COUNTER.load(Ordering::Relaxed); + loop { + let Some(id) = last.checked_add(1) else { + exhausted(); + }; + + match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { + Ok(_) => return ThreadId(NonZero::new(id).unwrap()), + Err(id) => last = id, + } + } + } + _ => { + use crate::sync::{Mutex, PoisonError}; + + static COUNTER: Mutex = Mutex::new(0); + + let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner); + let Some(id) = counter.checked_add(1) else { + // in case the panic handler ends up calling `ThreadId::new()`, + // avoid reentrant lock acquire. + drop(counter); + exhausted(); + }; + + *counter = id; + drop(counter); + ThreadId(NonZero::new(id).unwrap()) + } + } + } + + #[cfg(any(not(target_thread_local), target_has_atomic = "64"))] + pub(super) fn from_u64(v: u64) -> Option { + NonZero::new(v).map(ThreadId) + } + + /// This returns a numeric identifier for the thread identified by this + /// `ThreadId`. + /// + /// As noted in the documentation for the type itself, it is essentially an + /// opaque ID, but is guaranteed to be unique for each thread. The returned + /// value is entirely opaque -- only equality testing is stable. Note that + /// it is not guaranteed which values new threads will return, and this may + /// change across Rust versions. + #[must_use] + #[unstable(feature = "thread_id_value", issue = "67939")] + pub fn as_u64(&self) -> NonZero { + self.0 + } +} diff --git a/library/std/src/thread/join_handle.rs b/library/std/src/thread/join_handle.rs new file mode 100644 index 0000000000000..3ac80871fff98 --- /dev/null +++ b/library/std/src/thread/join_handle.rs @@ -0,0 +1,185 @@ +use super::Result; +use super::lifecycle::JoinInner; +use super::thread::Thread; +use crate::fmt; +use crate::sys::thread as imp; +use crate::sys_common::{AsInner, IntoInner}; + +/// An owned permission to join on a thread (block on its termination). +/// +/// A `JoinHandle` *detaches* the associated thread when it is dropped, which +/// means that there is no longer any handle to the thread and no way to `join` +/// on it. +/// +/// Due to platform restrictions, it is not possible to [`Clone`] this +/// handle: the ability to join a thread is a uniquely-owned permission. +/// +/// This `struct` is created by the [`thread::spawn`] function and the +/// [`thread::Builder::spawn`] method. +/// +/// # Examples +/// +/// Creation from [`thread::spawn`]: +/// +/// ``` +/// use std::thread; +/// +/// let join_handle: thread::JoinHandle<_> = thread::spawn(|| { +/// // some work here +/// }); +/// ``` +/// +/// Creation from [`thread::Builder::spawn`]: +/// +/// ``` +/// use std::thread; +/// +/// let builder = thread::Builder::new(); +/// +/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { +/// // some work here +/// }).unwrap(); +/// ``` +/// +/// A thread being detached and outliving the thread that spawned it: +/// +/// ```no_run +/// use std::thread; +/// use std::time::Duration; +/// +/// let original_thread = thread::spawn(|| { +/// let _detached_thread = thread::spawn(|| { +/// // Here we sleep to make sure that the first thread returns before. +/// thread::sleep(Duration::from_millis(10)); +/// // This will be called, even though the JoinHandle is dropped. +/// println!("♫ Still alive ♫"); +/// }); +/// }); +/// +/// original_thread.join().expect("The thread being joined has panicked"); +/// println!("Original thread is joined."); +/// +/// // We make sure that the new thread has time to run, before the main +/// // thread returns. +/// +/// thread::sleep(Duration::from_millis(1000)); +/// ``` +/// +/// [`thread::Builder::spawn`]: super::Builder::spawn +/// [`thread::spawn`]: super::spawn +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(target_os = "teeos", must_use)] +pub struct JoinHandle(pub(super) JoinInner<'static, T>); + +#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] +unsafe impl Send for JoinHandle {} +#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] +unsafe impl Sync for JoinHandle {} + +impl JoinHandle { + /// Extracts a handle to the underlying thread. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { + /// // some work here + /// }).unwrap(); + /// + /// let thread = join_handle.thread(); + /// println!("thread id: {:?}", thread.id()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub fn thread(&self) -> &Thread { + &self.0.thread() + } + + /// Waits for the associated thread to finish. + /// + /// This function will return immediately if the associated thread has already finished. + /// + /// In terms of [atomic memory orderings], the completion of the associated + /// thread synchronizes with this function returning. In other words, all + /// operations performed by that thread [happen + /// before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses) all + /// operations that happen after `join` returns. + /// + /// If the associated thread panics, [`Err`] is returned with the parameter given + /// to [`panic!`] (though see the Notes below). + /// + /// [`Err`]: crate::result::Result::Err + /// [atomic memory orderings]: crate::sync::atomic + /// + /// # Panics + /// + /// This function may panic on some platforms if a thread attempts to join + /// itself or otherwise may create a deadlock with joining threads. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { + /// // some work here + /// }).unwrap(); + /// join_handle.join().expect("Couldn't join on the associated thread"); + /// ``` + /// + /// # Notes + /// + /// If a "foreign" unwinding operation (e.g. an exception thrown from C++ + /// code, or a `panic!` in Rust code compiled or linked with a different + /// runtime) unwinds all the way to the thread root, the process may be + /// aborted; see the Notes on [`thread::spawn`]. If the process is not + /// aborted, this function will return a `Result::Err` containing an opaque + /// type. + /// + /// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html + /// [`thread::spawn`]: super::spawn + #[stable(feature = "rust1", since = "1.0.0")] + pub fn join(self) -> Result { + self.0.join() + } + + /// Checks if the associated thread has finished running its main function. + /// + /// `is_finished` supports implementing a non-blocking join operation, by checking + /// `is_finished`, and calling `join` if it returns `true`. This function does not block. To + /// block while waiting on the thread to finish, use [`join`][Self::join]. + /// + /// This might return `true` for a brief moment after the thread's main + /// function has returned, but before the thread itself has stopped running. + /// However, once this returns `true`, [`join`][Self::join] can be expected + /// to return quickly, without blocking for any significant amount of time. + #[stable(feature = "thread_is_running", since = "1.61.0")] + pub fn is_finished(&self) -> bool { + self.0.is_finished() + } +} + +impl AsInner for JoinHandle { + fn as_inner(&self) -> &imp::Thread { + &self.0.as_inner() + } +} + +impl IntoInner for JoinHandle { + fn into_inner(self) -> imp::Thread { + self.0.into_inner() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for JoinHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("JoinHandle").finish_non_exhaustive() + } +} diff --git a/library/std/src/thread/lifecycle.rs b/library/std/src/thread/lifecycle.rs new file mode 100644 index 0000000000000..fa43f885a8b07 --- /dev/null +++ b/library/std/src/thread/lifecycle.rs @@ -0,0 +1,240 @@ +//! The inner logic for thread spawning and joining. + +use super::current::set_current; +use super::id::ThreadId; +use super::scoped::ScopeData; +use super::thread::Thread; +use super::{Result, spawnhook}; +use crate::cell::UnsafeCell; +use crate::marker::PhantomData; +use crate::mem::{ManuallyDrop, MaybeUninit}; +use crate::sync::Arc; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; +use crate::sys::thread as imp; +use crate::sys_common::{AsInner, IntoInner}; +use crate::{env, io, panic}; + +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +pub(super) unsafe fn spawn_unchecked<'scope, F, T>( + name: Option, + stack_size: Option, + no_hooks: bool, + scope_data: Option>, + f: F, +) -> io::Result> +where + F: FnOnce() -> T, + F: Send, + T: Send, +{ + let stack_size = stack_size.unwrap_or_else(|| { + static MIN: Atomic = AtomicUsize::new(0); + + match MIN.load(Ordering::Relaxed) { + 0 => {} + n => return n - 1, + } + + let amt = env::var_os("RUST_MIN_STACK") + .and_then(|s| s.to_str().and_then(|s| s.parse().ok())) + .unwrap_or(imp::DEFAULT_MIN_STACK_SIZE); + + // 0 is our sentinel value, so ensure that we'll never see 0 after + // initialization has run + MIN.store(amt + 1, Ordering::Relaxed); + amt + }); + + let id = ThreadId::new(); + let my_thread = Thread::new(id, name); + + let hooks = if no_hooks { + spawnhook::ChildSpawnHooks::default() + } else { + spawnhook::run_spawn_hooks(&my_thread) + }; + + let their_thread = my_thread.clone(); + + let my_packet: Arc> = + Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None), _marker: PhantomData }); + let their_packet = my_packet.clone(); + + // Pass `f` in `MaybeUninit` because actually that closure might *run longer than the lifetime of `F`*. + // See for more details. + // To prevent leaks we use a wrapper that drops its contents. + #[repr(transparent)] + struct MaybeDangling(MaybeUninit); + impl MaybeDangling { + fn new(x: T) -> Self { + MaybeDangling(MaybeUninit::new(x)) + } + fn into_inner(self) -> T { + // Make sure we don't drop. + let this = ManuallyDrop::new(self); + // SAFETY: we are always initialized. + unsafe { this.0.assume_init_read() } + } + } + impl Drop for MaybeDangling { + fn drop(&mut self) { + // SAFETY: we are always initialized. + unsafe { self.0.assume_init_drop() }; + } + } + + let f = MaybeDangling::new(f); + let main = move || { + if let Err(_thread) = set_current(their_thread.clone()) { + // Both the current thread handle and the ID should not be + // initialized yet. Since only the C runtime and some of our + // platform code run before this, this point shouldn't be + // reachable. Use an abort to save binary size (see #123356). + rtabort!("something here is badly broken!"); + } + + if let Some(name) = their_thread.cname() { + imp::set_name(name); + } + + let f = f.into_inner(); + let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { + crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.run()); + crate::sys::backtrace::__rust_begin_short_backtrace(f) + })); + // SAFETY: `their_packet` as been built just above and moved by the + // closure (it is an Arc<...>) and `my_packet` will be stored in the + // same `JoinInner` as this closure meaning the mutation will be + // safe (not modify it and affect a value far away). + unsafe { *their_packet.result.get() = Some(try_result) }; + // Here `their_packet` gets dropped, and if this is the last `Arc` for that packet that + // will call `decrement_num_running_threads` and therefore signal that this thread is + // done. + drop(their_packet); + // Here, the lifetime `'scope` can end. `main` keeps running for a bit + // after that before returning itself. + }; + + if let Some(scope_data) = &my_packet.scope { + scope_data.increment_num_running_threads(); + } + + let main = Box::new(main); + // SAFETY: dynamic size and alignment of the Box remain the same. See below for why the + // lifetime change is justified. + let main = + unsafe { Box::from_raw(Box::into_raw(main) as *mut (dyn FnOnce() + Send + 'static)) }; + + Ok(JoinInner { + // SAFETY: + // + // `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed + // through FFI or otherwise used with low-level threading primitives that have no + // notion of or way to enforce lifetimes. + // + // As mentioned in the `Safety` section of this function's documentation, the caller of + // this function needs to guarantee that the passed-in lifetime is sufficiently long + // for the lifetime of the thread. + // + // Similarly, the `sys` implementation must guarantee that no references to the closure + // exist after the thread has terminated, which is signaled by `Thread::join` + // returning. + native: unsafe { imp::Thread::new(stack_size, my_thread.name(), main)? }, + thread: my_thread, + packet: my_packet, + }) +} + +// This packet is used to communicate the return value between the spawned +// thread and the rest of the program. It is shared through an `Arc` and +// there's no need for a mutex here because synchronization happens with `join()` +// (the caller will never read this packet until the thread has exited). +// +// An Arc to the packet is stored into a `JoinInner` which in turns is placed +// in `JoinHandle`. +struct Packet<'scope, T> { + scope: Option>, + result: UnsafeCell>>, + _marker: PhantomData>, +} + +// Due to the usage of `UnsafeCell` we need to manually implement Sync. +// The type `T` should already always be Send (otherwise the thread could not +// have been created) and the Packet is Sync because all access to the +// `UnsafeCell` synchronized (by the `join()` boundary), and `ScopeData` is Sync. +unsafe impl<'scope, T: Send> Sync for Packet<'scope, T> {} + +impl<'scope, T> Drop for Packet<'scope, T> { + fn drop(&mut self) { + // If this packet was for a thread that ran in a scope, the thread + // panicked, and nobody consumed the panic payload, we make sure + // the scope function will panic. + let unhandled_panic = matches!(self.result.get_mut(), Some(Err(_))); + // Drop the result without causing unwinding. + // This is only relevant for threads that aren't join()ed, as + // join() will take the `result` and set it to None, such that + // there is nothing left to drop here. + // If this panics, we should handle that, because we're outside the + // outermost `catch_unwind` of our thread. + // We just abort in that case, since there's nothing else we can do. + // (And even if we tried to handle it somehow, we'd also need to handle + // the case where the panic payload we get out of it also panics on + // drop, and so on. See issue #86027.) + if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| { + *self.result.get_mut() = None; + })) { + rtabort!("thread result panicked on drop"); + } + // Book-keeping so the scope knows when it's done. + if let Some(scope) = &self.scope { + // Now that there will be no more user code running on this thread + // that can use 'scope, mark the thread as 'finished'. + // It's important we only do this after the `result` has been dropped, + // since dropping it might still use things it borrowed from 'scope. + scope.decrement_num_running_threads(unhandled_panic); + } + } +} + +/// Inner representation for JoinHandle +pub(super) struct JoinInner<'scope, T> { + native: imp::Thread, + thread: Thread, + packet: Arc>, +} + +impl<'scope, T> JoinInner<'scope, T> { + pub(super) fn is_finished(&self) -> bool { + Arc::strong_count(&self.packet) == 1 + } + + pub(super) fn thread(&self) -> &Thread { + &self.thread + } + + pub(super) fn join(mut self) -> Result { + self.native.join(); + Arc::get_mut(&mut self.packet) + // FIXME(fuzzypixelz): returning an error instead of panicking here + // would require updating the documentation of + // `std::thread::Result`; currently we can return `Err` if and only + // if the thread had panicked. + .expect("threads should not terminate unexpectedly") + .result + .get_mut() + .take() + .unwrap() + } +} + +impl AsInner for JoinInner<'static, T> { + fn as_inner(&self) -> &imp::Thread { + &self.native + } +} + +impl IntoInner for JoinInner<'static, T> { + fn into_inner(self) -> imp::Thread { + self.native + } +} diff --git a/library/std/src/thread/main_thread.rs b/library/std/src/thread/main_thread.rs new file mode 100644 index 0000000000000..394074a593674 --- /dev/null +++ b/library/std/src/thread/main_thread.rs @@ -0,0 +1,56 @@ +//! Store the ID of the main thread. +//! +//! The thread handle for the main thread is created lazily, and this might even +//! happen pre-main. Since not every platform has a way to identify the main +//! thread when that happens – macOS's `pthread_main_np` function being a notable +//! exception – we cannot assign it the right name right then. Instead, in our +//! runtime startup code, we remember the thread ID of the main thread (through +//! this modules `set` function) and use it to identify the main thread from then +//! on. This works reliably and has the additional advantage that we can report +//! the right thread name on main even after the thread handle has been destroyed. +//! Note however that this also means that the name reported in pre-main functions +//! will be incorrect, but that's just something we have to live with. + +cfg_select! { + target_has_atomic = "64" => { + use super::id::ThreadId; + use crate::sync::atomic::{Atomic, AtomicU64}; + use crate::sync::atomic::Ordering::Relaxed; + + static MAIN: Atomic = AtomicU64::new(0); + + pub(super) fn get() -> Option { + ThreadId::from_u64(MAIN.load(Relaxed)) + } + + /// # Safety + /// May only be called once. + pub(crate) unsafe fn set(id: ThreadId) { + MAIN.store(id.as_u64().get(), Relaxed) + } + } + _ => { + use super::id::ThreadId; + use crate::mem::MaybeUninit; + use crate::sync::atomic::{Atomic, AtomicBool}; + use crate::sync::atomic::Ordering::{Acquire, Release}; + + static INIT: Atomic = AtomicBool::new(false); + static mut MAIN: MaybeUninit = MaybeUninit::uninit(); + + pub(super) fn get() -> Option { + if INIT.load(Acquire) { + Some(unsafe { MAIN.assume_init() }) + } else { + None + } + } + + /// # Safety + /// May only be called once. + pub(crate) unsafe fn set(id: ThreadId) { + unsafe { MAIN = MaybeUninit::new(id) }; + INIT.store(true, Release); + } + } +} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 16313da8e178c..dd150f76daa56 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -137,6 +137,7 @@ //! Note that the stack size of the main thread is *not* determined by Rust. //! //! [channels]: crate::sync::mpsc +//! [`Arc`]: crate::sync::Arc //! [`join`]: JoinHandle::join //! [`Result`]: crate::result::Result //! [`Ok`]: crate::result::Result::Ok @@ -155,53 +156,55 @@ // Under `test`, `__FastLocalKeyInner` seems unused. #![cfg_attr(test, allow(dead_code))] -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use crate::any::Any; -use crate::cell::UnsafeCell; -use crate::ffi::CStr; -use crate::marker::PhantomData; -use crate::mem::{self, ManuallyDrop, forget}; -use crate::num::NonZero; -use crate::pin::Pin; -use crate::sync::Arc; -use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; -use crate::sys::sync::Parker; -use crate::sys::thread as imp; -use crate::sys_common::{AsInner, IntoInner}; -use crate::time::{Duration, Instant}; -use crate::{env, fmt, io, panic, panicking, str}; -#[stable(feature = "scoped_threads", since = "1.63.0")] +#[macro_use] +mod local; +mod builder; +mod current; +mod functions; +mod id; +mod join_handle; +mod lifecycle; mod scoped; +mod spawnhook; +mod thread; -#[stable(feature = "scoped_threads", since = "1.63.0")] -pub use scoped::{Scope, ScopedJoinHandle, scope}; +pub(crate) mod main_thread; -mod current; +#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] +mod tests; +#[stable(feature = "rust1", since = "1.0.0")] +pub use builder::Builder; #[stable(feature = "rust1", since = "1.0.0")] pub use current::current; #[unstable(feature = "current_thread_id", issue = "147194")] pub use current::current_id; -pub(crate) use current::{current_or_unnamed, current_os_id, drop_current}; -use current::{set_current, try_with_current}; - -mod spawnhook; - +pub(crate) use current::{current_or_unnamed, current_os_id, drop_current, with_current_name}; +#[stable(feature = "available_parallelism", since = "1.59.0")] +pub use functions::available_parallelism; +#[stable(feature = "park_timeout", since = "1.4.0")] +pub use functions::park_timeout; +#[stable(feature = "thread_sleep", since = "1.4.0")] +pub use functions::sleep; +#[unstable(feature = "thread_sleep_until", issue = "113752")] +pub use functions::sleep_until; +#[expect(deprecated)] +#[stable(feature = "rust1", since = "1.0.0")] +pub use functions::{panicking, park, park_timeout_ms, sleep_ms, spawn, yield_now}; +#[stable(feature = "thread_id", since = "1.19.0")] +pub use id::ThreadId; +#[stable(feature = "rust1", since = "1.0.0")] +pub use join_handle::JoinHandle; +#[stable(feature = "rust1", since = "1.0.0")] +pub use local::{AccessError, LocalKey}; +#[stable(feature = "scoped_threads", since = "1.63.0")] +pub use scoped::{Scope, ScopedJoinHandle, scope}; #[unstable(feature = "thread_spawn_hook", issue = "132951")] pub use spawnhook::add_spawn_hook; - -//////////////////////////////////////////////////////////////////////////////// -// Thread-local storage -//////////////////////////////////////////////////////////////////////////////// - -#[macro_use] -mod local; - #[stable(feature = "rust1", since = "1.0.0")] -pub use self::local::{AccessError, LocalKey}; +pub use thread::Thread; // Implementation details used by the thread_local!{} macro. #[doc(hidden)] @@ -211,1494 +214,6 @@ pub mod local_impl { pub use crate::sys::thread_local::*; } -//////////////////////////////////////////////////////////////////////////////// -// Builder -//////////////////////////////////////////////////////////////////////////////// - -/// Thread factory, which can be used in order to configure the properties of -/// a new thread. -/// -/// Methods can be chained on it in order to configure it. -/// -/// The two configurations available are: -/// -/// - [`name`]: specifies an [associated name for the thread][naming-threads] -/// - [`stack_size`]: specifies the [desired stack size for the thread][stack-size] -/// -/// The [`spawn`] method will take ownership of the builder and create an -/// [`io::Result`] to the thread handle with the given configuration. -/// -/// The [`thread::spawn`] free function uses a `Builder` with default -/// configuration and [`unwrap`]s its return value. -/// -/// You may want to use [`spawn`] instead of [`thread::spawn`], when you want -/// to recover from a failure to launch a thread, indeed the free function will -/// panic where the `Builder` method will return a [`io::Result`]. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// -/// let builder = thread::Builder::new(); -/// -/// let handler = builder.spawn(|| { -/// // thread code -/// }).unwrap(); -/// -/// handler.join().unwrap(); -/// ``` -/// -/// [`stack_size`]: Builder::stack_size -/// [`name`]: Builder::name -/// [`spawn`]: Builder::spawn -/// [`thread::spawn`]: spawn -/// [`io::Result`]: crate::io::Result -/// [`unwrap`]: crate::result::Result::unwrap -/// [naming-threads]: ./index.html#naming-threads -/// [stack-size]: ./index.html#stack-size -#[must_use = "must eventually spawn the thread"] -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Builder { - // A name for the thread-to-be, for identification in panic messages - name: Option, - // The size of the stack for the spawned thread in bytes - stack_size: Option, - // Skip running and inheriting the thread spawn hooks - no_hooks: bool, -} - -impl Builder { - /// Generates the base configuration for spawning a thread, from which - /// configuration methods can be chained. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new() - /// .name("foo".into()) - /// .stack_size(32 * 1024); - /// - /// let handler = builder.spawn(|| { - /// // thread code - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> Builder { - Builder { name: None, stack_size: None, no_hooks: false } - } - - /// Names the thread-to-be. Currently the name is used for identification - /// only in panic messages. - /// - /// The name must not contain null bytes (`\0`). - /// - /// For more information about named threads, see - /// [this module-level documentation][naming-threads]. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new() - /// .name("foo".into()); - /// - /// let handler = builder.spawn(|| { - /// assert_eq!(thread::current().name(), Some("foo")) - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - /// - /// [naming-threads]: ./index.html#naming-threads - #[stable(feature = "rust1", since = "1.0.0")] - pub fn name(mut self, name: String) -> Builder { - self.name = Some(name); - self - } - - /// Sets the size of the stack (in bytes) for the new thread. - /// - /// The actual stack size may be greater than this value if - /// the platform specifies a minimal stack size. - /// - /// For more information about the stack size for threads, see - /// [this module-level documentation][stack-size]. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new().stack_size(32 * 1024); - /// ``` - /// - /// [stack-size]: ./index.html#stack-size - #[stable(feature = "rust1", since = "1.0.0")] - pub fn stack_size(mut self, size: usize) -> Builder { - self.stack_size = Some(size); - self - } - - /// Disables running and inheriting [spawn hooks](add_spawn_hook). - /// - /// Use this if the parent thread is in no way relevant for the child thread. - /// For example, when lazily spawning threads for a thread pool. - #[unstable(feature = "thread_spawn_hook", issue = "132951")] - pub fn no_hooks(mut self) -> Builder { - self.no_hooks = true; - self - } - - /// Spawns a new thread by taking ownership of the `Builder`, and returns an - /// [`io::Result`] to its [`JoinHandle`]. - /// - /// The spawned thread may outlive the caller (unless the caller thread - /// is the main thread; the whole process is terminated when the main - /// thread finishes). The join handle can be used to block on - /// termination of the spawned thread, including recovering its panics. - /// - /// For a more complete documentation see [`thread::spawn`][`spawn`]. - /// - /// # Errors - /// - /// Unlike the [`spawn`] free function, this method yields an - /// [`io::Result`] to capture any failure to create the thread at - /// the OS level. - /// - /// [`io::Result`]: crate::io::Result - /// - /// # Panics - /// - /// Panics if a thread name was set and it contained null bytes. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let handler = builder.spawn(|| { - /// // thread code - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub fn spawn(self, f: F) -> io::Result> - where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, - { - unsafe { self.spawn_unchecked(f) } - } - - /// Spawns a new thread without any lifetime restrictions by taking ownership - /// of the `Builder`, and returns an [`io::Result`] to its [`JoinHandle`]. - /// - /// The spawned thread may outlive the caller (unless the caller thread - /// is the main thread; the whole process is terminated when the main - /// thread finishes). The join handle can be used to block on - /// termination of the spawned thread, including recovering its panics. - /// - /// This method is identical to [`thread::Builder::spawn`][`Builder::spawn`], - /// except for the relaxed lifetime bounds, which render it unsafe. - /// For a more complete documentation see [`thread::spawn`][`spawn`]. - /// - /// # Errors - /// - /// Unlike the [`spawn`] free function, this method yields an - /// [`io::Result`] to capture any failure to create the thread at - /// the OS level. - /// - /// # Panics - /// - /// Panics if a thread name was set and it contained null bytes. - /// - /// # Safety - /// - /// The caller has to ensure that the spawned thread does not outlive any - /// references in the supplied thread closure and its return type. - /// This can be guaranteed in two ways: - /// - /// - ensure that [`join`][`JoinHandle::join`] is called before any referenced - /// data is dropped - /// - use only types with `'static` lifetime bounds, i.e., those with no or only - /// `'static` references (both [`thread::Builder::spawn`][`Builder::spawn`] - /// and [`thread::spawn`][`spawn`] enforce this property statically) - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let x = 1; - /// let thread_x = &x; - /// - /// let handler = unsafe { - /// builder.spawn_unchecked(move || { - /// println!("x = {}", *thread_x); - /// }).unwrap() - /// }; - /// - /// // caller has to ensure `join()` is called, otherwise - /// // it is possible to access freed memory if `x` gets - /// // dropped before the thread closure is executed! - /// handler.join().unwrap(); - /// ``` - /// - /// [`io::Result`]: crate::io::Result - #[stable(feature = "thread_spawn_unchecked", since = "1.82.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub unsafe fn spawn_unchecked(self, f: F) -> io::Result> - where - F: FnOnce() -> T, - F: Send, - T: Send, - { - Ok(JoinHandle(unsafe { self.spawn_unchecked_(f, None) }?)) - } - - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - unsafe fn spawn_unchecked_<'scope, F, T>( - self, - f: F, - scope_data: Option>, - ) -> io::Result> - where - F: FnOnce() -> T, - F: Send, - T: Send, - { - let Builder { name, stack_size, no_hooks } = self; - - let stack_size = stack_size.unwrap_or_else(|| { - static MIN: Atomic = AtomicUsize::new(0); - - match MIN.load(Ordering::Relaxed) { - 0 => {} - n => return n - 1, - } - - let amt = env::var_os("RUST_MIN_STACK") - .and_then(|s| s.to_str().and_then(|s| s.parse().ok())) - .unwrap_or(imp::DEFAULT_MIN_STACK_SIZE); - - // 0 is our sentinel value, so ensure that we'll never see 0 after - // initialization has run - MIN.store(amt + 1, Ordering::Relaxed); - amt - }); - - let id = ThreadId::new(); - let my_thread = Thread::new(id, name); - - let hooks = if no_hooks { - spawnhook::ChildSpawnHooks::default() - } else { - spawnhook::run_spawn_hooks(&my_thread) - }; - - let their_thread = my_thread.clone(); - - let my_packet: Arc> = Arc::new(Packet { - scope: scope_data, - result: UnsafeCell::new(None), - _marker: PhantomData, - }); - let their_packet = my_packet.clone(); - - // Pass `f` in `MaybeUninit` because actually that closure might *run longer than the lifetime of `F`*. - // See for more details. - // To prevent leaks we use a wrapper that drops its contents. - #[repr(transparent)] - struct MaybeDangling(mem::MaybeUninit); - impl MaybeDangling { - fn new(x: T) -> Self { - MaybeDangling(mem::MaybeUninit::new(x)) - } - fn into_inner(self) -> T { - // Make sure we don't drop. - let this = ManuallyDrop::new(self); - // SAFETY: we are always initialized. - unsafe { this.0.assume_init_read() } - } - } - impl Drop for MaybeDangling { - fn drop(&mut self) { - // SAFETY: we are always initialized. - unsafe { self.0.assume_init_drop() }; - } - } - - let f = MaybeDangling::new(f); - let main = move || { - if let Err(_thread) = set_current(their_thread.clone()) { - // Both the current thread handle and the ID should not be - // initialized yet. Since only the C runtime and some of our - // platform code run before this, this point shouldn't be - // reachable. Use an abort to save binary size (see #123356). - rtabort!("something here is badly broken!"); - } - - if let Some(name) = their_thread.cname() { - imp::set_name(name); - } - - let f = f.into_inner(); - let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { - crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.run()); - crate::sys::backtrace::__rust_begin_short_backtrace(f) - })); - // SAFETY: `their_packet` as been built just above and moved by the - // closure (it is an Arc<...>) and `my_packet` will be stored in the - // same `JoinInner` as this closure meaning the mutation will be - // safe (not modify it and affect a value far away). - unsafe { *their_packet.result.get() = Some(try_result) }; - // Here `their_packet` gets dropped, and if this is the last `Arc` for that packet that - // will call `decrement_num_running_threads` and therefore signal that this thread is - // done. - drop(their_packet); - // Here, the lifetime `'scope` can end. `main` keeps running for a bit - // after that before returning itself. - }; - - if let Some(scope_data) = &my_packet.scope { - scope_data.increment_num_running_threads(); - } - - let main = Box::new(main); - // SAFETY: dynamic size and alignment of the Box remain the same. See below for why the - // lifetime change is justified. - let main = - unsafe { Box::from_raw(Box::into_raw(main) as *mut (dyn FnOnce() + Send + 'static)) }; - - Ok(JoinInner { - // SAFETY: - // - // `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed - // through FFI or otherwise used with low-level threading primitives that have no - // notion of or way to enforce lifetimes. - // - // As mentioned in the `Safety` section of this function's documentation, the caller of - // this function needs to guarantee that the passed-in lifetime is sufficiently long - // for the lifetime of the thread. - // - // Similarly, the `sys` implementation must guarantee that no references to the closure - // exist after the thread has terminated, which is signaled by `Thread::join` - // returning. - native: unsafe { imp::Thread::new(stack_size, my_thread.name(), main)? }, - thread: my_thread, - packet: my_packet, - }) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Free functions -//////////////////////////////////////////////////////////////////////////////// - -/// Spawns a new thread, returning a [`JoinHandle`] for it. -/// -/// The join handle provides a [`join`] method that can be used to join the spawned -/// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing -/// the argument given to [`panic!`]. -/// -/// If the join handle is dropped, the spawned thread will implicitly be *detached*. -/// In this case, the spawned thread may no longer be joined. -/// (It is the responsibility of the program to either eventually join threads it -/// creates or detach them; otherwise, a resource leak will result.) -/// -/// This function creates a thread with the default parameters of [`Builder`]. -/// To specify the new thread's stack size or the name, use [`Builder::spawn`]. -/// -/// As you can see in the signature of `spawn` there are two constraints on -/// both the closure given to `spawn` and its return value, let's explain them: -/// -/// - The `'static` constraint means that the closure and its return value -/// must have a lifetime of the whole program execution. The reason for this -/// is that threads can outlive the lifetime they have been created in. -/// -/// Indeed if the thread, and by extension its return value, can outlive their -/// caller, we need to make sure that they will be valid afterwards, and since -/// we *can't* know when it will return we need to have them valid as long as -/// possible, that is until the end of the program, hence the `'static` -/// lifetime. -/// - The [`Send`] constraint is because the closure will need to be passed -/// *by value* from the thread where it is spawned to the new thread. Its -/// return value will need to be passed from the new thread to the thread -/// where it is `join`ed. -/// As a reminder, the [`Send`] marker trait expresses that it is safe to be -/// passed from thread to thread. [`Sync`] expresses that it is safe to have a -/// reference be passed from thread to thread. -/// -/// # Panics -/// -/// Panics if the OS fails to create a thread; use [`Builder::spawn`] -/// to recover from such errors. -/// -/// # Examples -/// -/// Creating a thread. -/// -/// ``` -/// use std::thread; -/// -/// let handler = thread::spawn(|| { -/// // thread code -/// }); -/// -/// handler.join().unwrap(); -/// ``` -/// -/// As mentioned in the module documentation, threads are usually made to -/// communicate using [`channels`], here is how it usually looks. -/// -/// This example also shows how to use `move`, in order to give ownership -/// of values to a thread. -/// -/// ``` -/// use std::thread; -/// use std::sync::mpsc::channel; -/// -/// let (tx, rx) = channel(); -/// -/// let sender = thread::spawn(move || { -/// tx.send("Hello, thread".to_owned()) -/// .expect("Unable to send on channel"); -/// }); -/// -/// let receiver = thread::spawn(move || { -/// let value = rx.recv().expect("Unable to receive from channel"); -/// println!("{value}"); -/// }); -/// -/// sender.join().expect("The sender thread has panicked"); -/// receiver.join().expect("The receiver thread has panicked"); -/// ``` -/// -/// A thread can also return a value through its [`JoinHandle`], you can use -/// this to make asynchronous computations (futures might be more appropriate -/// though). -/// -/// ``` -/// use std::thread; -/// -/// let computation = thread::spawn(|| { -/// // Some expensive computation. -/// 42 -/// }); -/// -/// let result = computation.join().unwrap(); -/// println!("{result}"); -/// ``` -/// -/// # Notes -/// -/// This function has the same minimal guarantee regarding "foreign" unwinding operations (e.g. -/// an exception thrown from C++ code, or a `panic!` in Rust code compiled or linked with a -/// different runtime) as [`catch_unwind`]; namely, if the thread created with `thread::spawn` -/// unwinds all the way to the root with such an exception, one of two behaviors are possible, -/// and it is unspecified which will occur: -/// -/// * The process aborts. -/// * The process does not abort, and [`join`] will return a `Result::Err` -/// containing an opaque type. -/// -/// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html -/// [`channels`]: crate::sync::mpsc -/// [`join`]: JoinHandle::join -/// [`Err`]: crate::result::Result::Err -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -pub fn spawn(f: F) -> JoinHandle -where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, -{ - Builder::new().spawn(f).expect("failed to spawn thread") -} - -/// Cooperatively gives up a timeslice to the OS scheduler. -/// -/// This calls the underlying OS scheduler's yield primitive, signaling -/// that the calling thread is willing to give up its remaining timeslice -/// so that the OS may schedule other threads on the CPU. -/// -/// A drawback of yielding in a loop is that if the OS does not have any -/// other ready threads to run on the current CPU, the thread will effectively -/// busy-wait, which wastes CPU time and energy. -/// -/// Therefore, when waiting for events of interest, a programmer's first -/// choice should be to use synchronization devices such as [`channel`]s, -/// [`Condvar`]s, [`Mutex`]es or [`join`] since these primitives are -/// implemented in a blocking manner, giving up the CPU until the event -/// of interest has occurred which avoids repeated yielding. -/// -/// `yield_now` should thus be used only rarely, mostly in situations where -/// repeated polling is required because there is no other suitable way to -/// learn when an event of interest has occurred. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// -/// thread::yield_now(); -/// ``` -/// -/// [`channel`]: crate::sync::mpsc -/// [`join`]: JoinHandle::join -/// [`Condvar`]: crate::sync::Condvar -/// [`Mutex`]: crate::sync::Mutex -#[stable(feature = "rust1", since = "1.0.0")] -pub fn yield_now() { - imp::yield_now() -} - -/// Determines whether the current thread is unwinding because of panic. -/// -/// A common use of this feature is to poison shared resources when writing -/// unsafe code, by checking `panicking` when the `drop` is called. -/// -/// This is usually not needed when writing safe code, as [`Mutex`es][Mutex] -/// already poison themselves when a thread panics while holding the lock. -/// -/// This can also be used in multithreaded applications, in order to send a -/// message to other threads warning that a thread has panicked (e.g., for -/// monitoring purposes). -/// -/// # Examples -/// -/// ```should_panic -/// use std::thread; -/// -/// struct SomeStruct; -/// -/// impl Drop for SomeStruct { -/// fn drop(&mut self) { -/// if thread::panicking() { -/// println!("dropped while unwinding"); -/// } else { -/// println!("dropped while not unwinding"); -/// } -/// } -/// } -/// -/// { -/// print!("a: "); -/// let a = SomeStruct; -/// } -/// -/// { -/// print!("b: "); -/// let b = SomeStruct; -/// panic!() -/// } -/// ``` -/// -/// [Mutex]: crate::sync::Mutex -#[inline] -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn panicking() -> bool { - panicking::panicking() -} - -/// Uses [`sleep`]. -/// -/// Puts the current thread to sleep for at least the specified amount of time. -/// -/// The thread may sleep longer than the duration specified due to scheduling -/// specifics or platform-dependent functionality. It will never sleep less. -/// -/// This function is blocking, and should not be used in `async` functions. -/// -/// # Platform-specific behavior -/// -/// On Unix platforms, the underlying syscall may be interrupted by a -/// spurious wakeup or signal handler. To ensure the sleep occurs for at least -/// the specified duration, this function may invoke that system call multiple -/// times. -/// -/// # Examples -/// -/// ```no_run -/// use std::thread; -/// -/// // Let's sleep for 2 seconds: -/// thread::sleep_ms(2000); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[deprecated(since = "1.6.0", note = "replaced by `std::thread::sleep`")] -pub fn sleep_ms(ms: u32) { - sleep(Duration::from_millis(ms as u64)) -} - -/// Puts the current thread to sleep for at least the specified amount of time. -/// -/// The thread may sleep longer than the duration specified due to scheduling -/// specifics or platform-dependent functionality. It will never sleep less. -/// -/// This function is blocking, and should not be used in `async` functions. -/// -/// # Platform-specific behavior -/// -/// On Unix platforms, the underlying syscall may be interrupted by a -/// spurious wakeup or signal handler. To ensure the sleep occurs for at least -/// the specified duration, this function may invoke that system call multiple -/// times. -/// Platforms which do not support nanosecond precision for sleeping will -/// have `dur` rounded up to the nearest granularity of time they can sleep for. -/// -/// Currently, specifying a zero duration on Unix platforms returns immediately -/// without invoking the underlying [`nanosleep`] syscall, whereas on Windows -/// platforms the underlying [`Sleep`] syscall is always invoked. -/// If the intention is to yield the current time-slice you may want to use -/// [`yield_now`] instead. -/// -/// [`nanosleep`]: https://linux.die.net/man/2/nanosleep -/// [`Sleep`]: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep -/// -/// # Examples -/// -/// ```no_run -/// use std::{thread, time}; -/// -/// let ten_millis = time::Duration::from_millis(10); -/// let now = time::Instant::now(); -/// -/// thread::sleep(ten_millis); -/// -/// assert!(now.elapsed() >= ten_millis); -/// ``` -#[stable(feature = "thread_sleep", since = "1.4.0")] -pub fn sleep(dur: Duration) { - imp::sleep(dur) -} - -/// Puts the current thread to sleep until the specified deadline has passed. -/// -/// The thread may still be asleep after the deadline specified due to -/// scheduling specifics or platform-dependent functionality. It will never -/// wake before. -/// -/// This function is blocking, and should not be used in `async` functions. -/// -/// # Platform-specific behavior -/// -/// In most cases this function will call an OS specific function. Where that -/// is not supported [`sleep`] is used. Those platforms are referred to as other -/// in the table below. -/// -/// # Underlying System calls -/// -/// The following system calls are [currently] being used: -/// -/// | Platform | System call | -/// |-----------|----------------------------------------------------------------------| -/// | Linux | [clock_nanosleep] (Monotonic clock) | -/// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock)] | -/// | Android | [clock_nanosleep] (Monotonic Clock)] | -/// | Solaris | [clock_nanosleep] (Monotonic Clock)] | -/// | Illumos | [clock_nanosleep] (Monotonic Clock)] | -/// | Dragonfly | [clock_nanosleep] (Monotonic Clock)] | -/// | Hurd | [clock_nanosleep] (Monotonic Clock)] | -/// | Fuchsia | [clock_nanosleep] (Monotonic Clock)] | -/// | Vxworks | [clock_nanosleep] (Monotonic Clock)] | -/// | Other | `sleep_until` uses [`sleep`] and does not issue a syscall itself | -/// -/// [currently]: crate::io#platform-specific-behavior -/// [clock_nanosleep]: https://linux.die.net/man/3/clock_nanosleep -/// -/// **Disclaimer:** These system calls might change over time. -/// -/// # Examples -/// -/// A simple game loop that limits the game to 60 frames per second. -/// -/// ```no_run -/// #![feature(thread_sleep_until)] -/// # use std::time::{Duration, Instant}; -/// # use std::thread; -/// # -/// # fn update() {} -/// # fn render() {} -/// # -/// let max_fps = 60.0; -/// let frame_time = Duration::from_secs_f32(1.0/max_fps); -/// let mut next_frame = Instant::now(); -/// loop { -/// thread::sleep_until(next_frame); -/// next_frame += frame_time; -/// update(); -/// render(); -/// } -/// ``` -/// -/// A slow API we must not call too fast and which takes a few -/// tries before succeeding. By using `sleep_until` the time the -/// API call takes does not influence when we retry or when we give up -/// -/// ```no_run -/// #![feature(thread_sleep_until)] -/// # use std::time::{Duration, Instant}; -/// # use std::thread; -/// # -/// # enum Status { -/// # Ready(usize), -/// # Waiting, -/// # } -/// # fn slow_web_api_call() -> Status { Status::Ready(42) } -/// # -/// # const MAX_DURATION: Duration = Duration::from_secs(10); -/// # -/// # fn try_api_call() -> Result { -/// let deadline = Instant::now() + MAX_DURATION; -/// let delay = Duration::from_millis(250); -/// let mut next_attempt = Instant::now(); -/// loop { -/// if Instant::now() > deadline { -/// break Err(()); -/// } -/// if let Status::Ready(data) = slow_web_api_call() { -/// break Ok(data); -/// } -/// -/// next_attempt = deadline.min(next_attempt + delay); -/// thread::sleep_until(next_attempt); -/// } -/// # } -/// # let _data = try_api_call(); -/// ``` -#[unstable(feature = "thread_sleep_until", issue = "113752")] -pub fn sleep_until(deadline: Instant) { - imp::sleep_until(deadline) -} - -/// Used to ensure that `park` and `park_timeout` do not unwind, as that can -/// cause undefined behavior if not handled correctly (see #102398 for context). -struct PanicGuard; - -impl Drop for PanicGuard { - fn drop(&mut self) { - rtabort!("an irrecoverable error occurred while synchronizing threads") - } -} - -/// Blocks unless or until the current thread's token is made available. -/// -/// A call to `park` does not guarantee that the thread will remain parked -/// forever, and callers should be prepared for this possibility. However, -/// it is guaranteed that this function will not panic (it may abort the -/// process if the implementation encounters some rare errors). -/// -/// # `park` and `unpark` -/// -/// Every thread is equipped with some basic low-level blocking support, via the -/// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`] -/// method. [`park`] blocks the current thread, which can then be resumed from -/// another thread by calling the [`unpark`] method on the blocked thread's -/// handle. -/// -/// Conceptually, each [`Thread`] handle has an associated token, which is -/// initially not present: -/// -/// * The [`thread::park`][`park`] function blocks the current thread unless or -/// until the token is available for its thread handle, at which point it -/// atomically consumes the token. It may also return *spuriously*, without -/// consuming the token. [`thread::park_timeout`] does the same, but allows -/// specifying a maximum time to block the thread for. -/// -/// * The [`unpark`] method on a [`Thread`] atomically makes the token available -/// if it wasn't already. Because the token can be held by a thread even if it is currently not -/// parked, [`unpark`] followed by [`park`] will result in the second call returning immediately. -/// However, note that to rely on this guarantee, you need to make sure that your `unpark` happens -/// after all `park` that may be done by other data structures! -/// -/// The API is typically used by acquiring a handle to the current thread, placing that handle in a -/// shared data structure so that other threads can find it, and then `park`ing in a loop. When some -/// desired condition is met, another thread calls [`unpark`] on the handle. The last bullet point -/// above guarantees that even if the `unpark` occurs before the thread is finished `park`ing, it -/// will be woken up properly. -/// -/// Note that the coordination via the shared data structure is crucial: If you `unpark` a thread -/// without first establishing that it is about to be `park`ing within your code, that `unpark` may -/// get consumed by a *different* `park` in the same thread, leading to a deadlock. This also means -/// you must not call unknown code between setting up for parking and calling `park`; for instance, -/// if you invoke `println!`, that may itself call `park` and thus consume your `unpark` and cause a -/// deadlock. -/// -/// The motivation for this design is twofold: -/// -/// * It avoids the need to allocate mutexes and condvars when building new -/// synchronization primitives; the threads already provide basic -/// blocking/signaling. -/// -/// * It can be implemented very efficiently on many platforms. -/// -/// # Memory Ordering -/// -/// Calls to `unpark` _synchronize-with_ calls to `park`, meaning that memory -/// operations performed before a call to `unpark` are made visible to the thread that -/// consumes the token and returns from `park`. Note that all `park` and `unpark` -/// operations for a given thread form a total order and _all_ prior `unpark` operations -/// synchronize-with `park`. -/// -/// In atomic ordering terms, `unpark` performs a `Release` operation and `park` -/// performs the corresponding `Acquire` operation. Calls to `unpark` for the same -/// thread form a [release sequence]. -/// -/// Note that being unblocked does not imply a call was made to `unpark`, because -/// wakeups can also be spurious. For example, a valid, but inefficient, -/// implementation could have `park` and `unpark` return immediately without doing anything, -/// making *all* wakeups spurious. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// use std::sync::atomic::{Ordering, AtomicBool}; -/// use std::time::Duration; -/// -/// static QUEUED: AtomicBool = AtomicBool::new(false); -/// static FLAG: AtomicBool = AtomicBool::new(false); -/// -/// let parked_thread = thread::spawn(move || { -/// println!("Thread spawned"); -/// // Signal that we are going to `park`. Between this store and our `park`, there may -/// // be no other `park`, or else that `park` could consume our `unpark` token! -/// QUEUED.store(true, Ordering::Release); -/// // We want to wait until the flag is set. We *could* just spin, but using -/// // park/unpark is more efficient. -/// while !FLAG.load(Ordering::Acquire) { -/// // We can *not* use `println!` here since that could use thread parking internally. -/// thread::park(); -/// // We *could* get here spuriously, i.e., way before the 10ms below are over! -/// // But that is no problem, we are in a loop until the flag is set anyway. -/// } -/// println!("Flag received"); -/// }); -/// -/// // Let some time pass for the thread to be spawned. -/// thread::sleep(Duration::from_millis(10)); -/// -/// // Ensure the thread is about to park. -/// // This is crucial! It guarantees that the `unpark` below is not consumed -/// // by some other code in the parked thread (e.g. inside `println!`). -/// while !QUEUED.load(Ordering::Acquire) { -/// // Spinning is of course inefficient; in practice, this would more likely be -/// // a dequeue where we have no work to do if there's nobody queued. -/// std::hint::spin_loop(); -/// } -/// -/// // Set the flag, and let the thread wake up. -/// // There is no race condition here: if `unpark` -/// // happens first, `park` will return immediately. -/// // There is also no other `park` that could consume this token, -/// // since we waited until the other thread got queued. -/// // Hence there is no risk of a deadlock. -/// FLAG.store(true, Ordering::Release); -/// println!("Unpark the thread"); -/// parked_thread.thread().unpark(); -/// -/// parked_thread.join().unwrap(); -/// ``` -/// -/// [`unpark`]: Thread::unpark -/// [`thread::park_timeout`]: park_timeout -/// [release sequence]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release_sequence -#[stable(feature = "rust1", since = "1.0.0")] -pub fn park() { - let guard = PanicGuard; - // SAFETY: park_timeout is called on the parker owned by this thread. - unsafe { - current().park(); - } - // No panic occurred, do not abort. - forget(guard); -} - -/// Uses [`park_timeout`]. -/// -/// Blocks unless or until the current thread's token is made available or -/// the specified duration has been reached (may wake spuriously). -/// -/// The semantics of this function are equivalent to [`park`] 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 `ms` long. -/// -/// See the [park documentation][`park`] for more detail. -#[stable(feature = "rust1", since = "1.0.0")] -#[deprecated(since = "1.6.0", note = "replaced by `std::thread::park_timeout`")] -pub fn park_timeout_ms(ms: u32) { - park_timeout(Duration::from_millis(ms as u64)) -} - -/// Blocks unless or until the current thread's token is made available or -/// the specified duration has been reached (may wake spuriously). -/// -/// The semantics of this function are equivalent to [`park`][park] 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` long. -/// -/// See the [park documentation][park] for more details. -/// -/// # Platform-specific behavior -/// -/// Platforms which do not support nanosecond precision for sleeping will have -/// `dur` rounded up to the nearest granularity of time they can sleep for. -/// -/// # Examples -/// -/// Waiting for the complete expiration of the timeout: -/// -/// ```rust,no_run -/// use std::thread::park_timeout; -/// use std::time::{Instant, Duration}; -/// -/// let timeout = Duration::from_secs(2); -/// let beginning_park = Instant::now(); -/// -/// let mut timeout_remaining = timeout; -/// loop { -/// park_timeout(timeout_remaining); -/// let elapsed = beginning_park.elapsed(); -/// if elapsed >= timeout { -/// break; -/// } -/// println!("restarting park_timeout after {elapsed:?}"); -/// timeout_remaining = timeout - elapsed; -/// } -/// ``` -#[stable(feature = "park_timeout", since = "1.4.0")] -pub fn park_timeout(dur: Duration) { - let guard = PanicGuard; - // SAFETY: park_timeout is called on a handle owned by this thread. - unsafe { - current().park_timeout(dur); - } - // No panic occurred, do not abort. - forget(guard); -} - -//////////////////////////////////////////////////////////////////////////////// -// ThreadId -//////////////////////////////////////////////////////////////////////////////// - -/// A unique identifier for a running thread. -/// -/// A `ThreadId` is an opaque object that uniquely identifies each thread -/// created during the lifetime of a process. `ThreadId`s are guaranteed not to -/// be reused, even when a thread terminates. `ThreadId`s are under the control -/// of Rust's standard library and there may not be any relationship between -/// `ThreadId` and the underlying platform's notion of a thread identifier -- -/// the two concepts cannot, therefore, be used interchangeably. A `ThreadId` -/// can be retrieved from the [`id`] method on a [`Thread`]. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// -/// let other_thread = thread::spawn(|| { -/// thread::current().id() -/// }); -/// -/// let other_thread_id = other_thread.join().unwrap(); -/// assert!(thread::current().id() != other_thread_id); -/// ``` -/// -/// [`id`]: Thread::id -#[stable(feature = "thread_id", since = "1.19.0")] -#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] -pub struct ThreadId(NonZero); - -impl ThreadId { - // Generate a new unique thread ID. - pub(crate) fn new() -> ThreadId { - #[cold] - fn exhausted() -> ! { - panic!("failed to generate unique thread ID: bitspace exhausted") - } - - cfg_select! { - target_has_atomic = "64" => { - use crate::sync::atomic::{Atomic, AtomicU64}; - - static COUNTER: Atomic = AtomicU64::new(0); - - let mut last = COUNTER.load(Ordering::Relaxed); - loop { - let Some(id) = last.checked_add(1) else { - exhausted(); - }; - - match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - Ok(_) => return ThreadId(NonZero::new(id).unwrap()), - Err(id) => last = id, - } - } - } - _ => { - use crate::sync::{Mutex, PoisonError}; - - static COUNTER: Mutex = Mutex::new(0); - - let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner); - let Some(id) = counter.checked_add(1) else { - // in case the panic handler ends up calling `ThreadId::new()`, - // avoid reentrant lock acquire. - drop(counter); - exhausted(); - }; - - *counter = id; - drop(counter); - ThreadId(NonZero::new(id).unwrap()) - } - } - } - - #[cfg(any(not(target_thread_local), target_has_atomic = "64"))] - fn from_u64(v: u64) -> Option { - NonZero::new(v).map(ThreadId) - } - - /// This returns a numeric identifier for the thread identified by this - /// `ThreadId`. - /// - /// As noted in the documentation for the type itself, it is essentially an - /// opaque ID, but is guaranteed to be unique for each thread. The returned - /// value is entirely opaque -- only equality testing is stable. Note that - /// it is not guaranteed which values new threads will return, and this may - /// change across Rust versions. - #[must_use] - #[unstable(feature = "thread_id_value", issue = "67939")] - pub fn as_u64(&self) -> NonZero { - self.0 - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Thread -//////////////////////////////////////////////////////////////////////////////// - -// This module ensures private fields are kept private, which is necessary to enforce the safety requirements. -mod thread_name_string { - use crate::ffi::{CStr, CString}; - use crate::str; - - /// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated. - pub(crate) struct ThreadNameString { - inner: CString, - } - - impl From for ThreadNameString { - fn from(s: String) -> Self { - Self { - inner: CString::new(s).expect("thread name may not contain interior null bytes"), - } - } - } - - impl ThreadNameString { - pub fn as_cstr(&self) -> &CStr { - &self.inner - } - - pub fn as_str(&self) -> &str { - // SAFETY: `ThreadNameString` is guaranteed to be UTF-8. - unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) } - } - } -} - -use thread_name_string::ThreadNameString; - -/// Store the ID of the main thread. -/// -/// The thread handle for the main thread is created lazily, and this might even -/// happen pre-main. Since not every platform has a way to identify the main -/// thread when that happens – macOS's `pthread_main_np` function being a notable -/// exception – we cannot assign it the right name right then. Instead, in our -/// runtime startup code, we remember the thread ID of the main thread (through -/// this modules `set` function) and use it to identify the main thread from then -/// on. This works reliably and has the additional advantage that we can report -/// the right thread name on main even after the thread handle has been destroyed. -/// Note however that this also means that the name reported in pre-main functions -/// will be incorrect, but that's just something we have to live with. -pub(crate) mod main_thread { - cfg_select! { - target_has_atomic = "64" => { - use super::ThreadId; - use crate::sync::atomic::{Atomic, AtomicU64}; - use crate::sync::atomic::Ordering::Relaxed; - - static MAIN: Atomic = AtomicU64::new(0); - - pub(super) fn get() -> Option { - ThreadId::from_u64(MAIN.load(Relaxed)) - } - - /// # Safety - /// May only be called once. - pub(crate) unsafe fn set(id: ThreadId) { - MAIN.store(id.as_u64().get(), Relaxed) - } - } - _ => { - use super::ThreadId; - use crate::mem::MaybeUninit; - use crate::sync::atomic::{Atomic, AtomicBool}; - use crate::sync::atomic::Ordering::{Acquire, Release}; - - static INIT: Atomic = AtomicBool::new(false); - static mut MAIN: MaybeUninit = MaybeUninit::uninit(); - - pub(super) fn get() -> Option { - if INIT.load(Acquire) { - Some(unsafe { MAIN.assume_init() }) - } else { - None - } - } - - /// # Safety - /// May only be called once. - pub(crate) unsafe fn set(id: ThreadId) { - unsafe { MAIN = MaybeUninit::new(id) }; - INIT.store(true, Release); - } - } - } -} - -/// Run a function with the current thread's name. -/// -/// Modulo thread local accesses, this function is safe to call from signal -/// handlers and in similar circumstances where allocations are not possible. -pub(crate) fn with_current_name(f: F) -> R -where - F: FnOnce(Option<&str>) -> R, -{ - try_with_current(|thread| { - if let Some(thread) = thread { - // If there is a current thread handle, try to use the name stored - // there. - if let Some(name) = &thread.inner.name { - return f(Some(name.as_str())); - } else if Some(thread.inner.id) == main_thread::get() { - // The main thread doesn't store its name in the handle, we must - // identify it through its ID. Since we already have the `Thread`, - // we can retrieve the ID from it instead of going through another - // thread local. - return f(Some("main")); - } - } else if let Some(main) = main_thread::get() - && let Some(id) = current::id::get() - && id == main - { - // The main thread doesn't always have a thread handle, we must - // identify it through its ID instead. The checks are ordered so - // that the current ID is only loaded if it is actually needed, - // since loading it from TLS might need multiple expensive accesses. - return f(Some("main")); - } - - f(None) - }) -} - -/// The internal representation of a `Thread` handle -/// -/// We explicitly set the alignment for our guarantee in Thread::into_raw. This -/// allows applications to stuff extra metadata bits into the alignment, which -/// can be rather useful when working with atomics. -#[repr(align(8))] -struct Inner { - name: Option, - id: ThreadId, - parker: Parker, -} - -impl Inner { - fn parker(self: Pin<&Self>) -> Pin<&Parker> { - unsafe { Pin::map_unchecked(self, |inner| &inner.parker) } - } -} - -#[derive(Clone)] -#[stable(feature = "rust1", since = "1.0.0")] -/// A handle to a thread. -/// -/// Threads are represented via the `Thread` type, which you can get in one of -/// two ways: -/// -/// * By spawning a new thread, e.g., using the [`thread::spawn`][`spawn`] -/// function, and calling [`thread`][`JoinHandle::thread`] on the -/// [`JoinHandle`]. -/// * By requesting the current thread, using the [`thread::current`] function. -/// -/// The [`thread::current`] function is available even for threads not spawned -/// by the APIs of this module. -/// -/// There is usually no need to create a `Thread` struct yourself, one -/// should instead use a function like `spawn` to create new threads, see the -/// docs of [`Builder`] and [`spawn`] for more details. -/// -/// [`thread::current`]: current::current -pub struct Thread { - inner: Pin>, -} - -impl Thread { - pub(crate) fn new(id: ThreadId, name: Option) -> Thread { - let name = name.map(ThreadNameString::from); - - // We have to use `unsafe` here to construct the `Parker` in-place, - // which is required for the UNIX implementation. - // - // SAFETY: We pin the Arc immediately after creation, so its address never - // changes. - let inner = unsafe { - let mut arc = Arc::::new_uninit(); - let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr(); - (&raw mut (*ptr).name).write(name); - (&raw mut (*ptr).id).write(id); - Parker::new_in_place(&raw mut (*ptr).parker); - Pin::new_unchecked(arc.assume_init()) - }; - - Thread { inner } - } - - /// Like the public [`park`], but callable on any handle. This is used to - /// allow parking in TLS destructors. - /// - /// # Safety - /// May only be called from the thread to which this handle belongs. - pub(crate) unsafe fn park(&self) { - unsafe { self.inner.as_ref().parker().park() } - } - - /// Like the public [`park_timeout`], but callable on any handle. This is - /// used to allow parking in TLS destructors. - /// - /// # Safety - /// May only be called from the thread to which this handle belongs. - pub(crate) unsafe fn park_timeout(&self, dur: Duration) { - unsafe { self.inner.as_ref().parker().park_timeout(dur) } - } - - /// Atomically makes the handle's token available if it is not already. - /// - /// Every thread is equipped with some basic low-level blocking support, via - /// the [`park`][park] function and the `unpark()` method. These can be - /// used as a more CPU-efficient implementation of a spinlock. - /// - /// See the [park documentation][park] for more details. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// use std::time::Duration; - /// use std::sync::atomic::{AtomicBool, Ordering}; - /// - /// static QUEUED: AtomicBool = AtomicBool::new(false); - /// - /// let parked_thread = thread::Builder::new() - /// .spawn(|| { - /// println!("Parking thread"); - /// QUEUED.store(true, Ordering::Release); - /// thread::park(); - /// println!("Thread unparked"); - /// }) - /// .unwrap(); - /// - /// // Let some time pass for the thread to be spawned. - /// thread::sleep(Duration::from_millis(10)); - /// - /// // Wait until the other thread is queued. - /// // This is crucial! It guarantees that the `unpark` below is not consumed - /// // by some other code in the parked thread (e.g. inside `println!`). - /// while !QUEUED.load(Ordering::Acquire) { - /// // Spinning is of course inefficient; in practice, this would more likely be - /// // a dequeue where we have no work to do if there's nobody queued. - /// std::hint::spin_loop(); - /// } - /// - /// println!("Unpark the thread"); - /// parked_thread.thread().unpark(); - /// - /// parked_thread.join().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn unpark(&self) { - self.inner.as_ref().parker().unpark(); - } - - /// Gets the thread's unique identifier. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let other_thread = thread::spawn(|| { - /// thread::current().id() - /// }); - /// - /// let other_thread_id = other_thread.join().unwrap(); - /// assert!(thread::current().id() != other_thread_id); - /// ``` - #[stable(feature = "thread_id", since = "1.19.0")] - #[must_use] - pub fn id(&self) -> ThreadId { - self.inner.id - } - - /// Gets the thread's name. - /// - /// For more information about named threads, see - /// [this module-level documentation][naming-threads]. - /// - /// # Examples - /// - /// Threads by default have no name specified: - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let handler = builder.spawn(|| { - /// assert!(thread::current().name().is_none()); - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - /// - /// Thread with a specified name: - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new() - /// .name("foo".into()); - /// - /// let handler = builder.spawn(|| { - /// assert_eq!(thread::current().name(), Some("foo")) - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - /// - /// [naming-threads]: ./index.html#naming-threads - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - pub fn name(&self) -> Option<&str> { - if let Some(name) = &self.inner.name { - Some(name.as_str()) - } else if main_thread::get() == Some(self.inner.id) { - Some("main") - } else { - None - } - } - - /// Consumes the `Thread`, returning a raw pointer. - /// - /// To avoid a memory leak the pointer must be converted - /// back into a `Thread` using [`Thread::from_raw`]. The pointer is - /// guaranteed to be aligned to at least 8 bytes. - /// - /// # Examples - /// - /// ``` - /// #![feature(thread_raw)] - /// - /// use std::thread::{self, Thread}; - /// - /// let thread = thread::current(); - /// let id = thread.id(); - /// let ptr = Thread::into_raw(thread); - /// unsafe { - /// assert_eq!(Thread::from_raw(ptr).id(), id); - /// } - /// ``` - #[unstable(feature = "thread_raw", issue = "97523")] - pub fn into_raw(self) -> *const () { - // Safety: We only expose an opaque pointer, which maintains the `Pin` invariant. - let inner = unsafe { Pin::into_inner_unchecked(self.inner) }; - Arc::into_raw(inner) as *const () - } - - /// Constructs a `Thread` from a raw pointer. - /// - /// The raw pointer must have been previously returned - /// by a call to [`Thread::into_raw`]. - /// - /// # Safety - /// - /// This function is unsafe because improper use may lead - /// to memory unsafety, even if the returned `Thread` is never - /// accessed. - /// - /// Creating a `Thread` from a pointer other than one returned - /// from [`Thread::into_raw`] is **undefined behavior**. - /// - /// Calling this function twice on the same raw pointer can lead - /// to a double-free if both `Thread` instances are dropped. - #[unstable(feature = "thread_raw", issue = "97523")] - pub unsafe fn from_raw(ptr: *const ()) -> Thread { - // Safety: Upheld by caller. - unsafe { Thread { inner: Pin::new_unchecked(Arc::from_raw(ptr as *const Inner)) } } - } - - fn cname(&self) -> Option<&CStr> { - if let Some(name) = &self.inner.name { - Some(name.as_cstr()) - } else if main_thread::get() == Some(self.inner.id) { - Some(c"main") - } else { - None - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Thread { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Thread") - .field("id", &self.id()) - .field("name", &self.name()) - .finish_non_exhaustive() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// JoinHandle -//////////////////////////////////////////////////////////////////////////////// - /// A specialized [`Result`] type for threads. /// /// Indicates the manner in which a thread exited. @@ -1745,353 +260,8 @@ impl fmt::Debug for Thread { #[doc(search_unbox)] pub type Result = crate::result::Result>; -// This packet is used to communicate the return value between the spawned -// thread and the rest of the program. It is shared through an `Arc` and -// there's no need for a mutex here because synchronization happens with `join()` -// (the caller will never read this packet until the thread has exited). -// -// An Arc to the packet is stored into a `JoinInner` which in turns is placed -// in `JoinHandle`. -struct Packet<'scope, T> { - scope: Option>, - result: UnsafeCell>>, - _marker: PhantomData>, -} - -// Due to the usage of `UnsafeCell` we need to manually implement Sync. -// The type `T` should already always be Send (otherwise the thread could not -// have been created) and the Packet is Sync because all access to the -// `UnsafeCell` synchronized (by the `join()` boundary), and `ScopeData` is Sync. -unsafe impl<'scope, T: Send> Sync for Packet<'scope, T> {} - -impl<'scope, T> Drop for Packet<'scope, T> { - fn drop(&mut self) { - // If this packet was for a thread that ran in a scope, the thread - // panicked, and nobody consumed the panic payload, we make sure - // the scope function will panic. - let unhandled_panic = matches!(self.result.get_mut(), Some(Err(_))); - // Drop the result without causing unwinding. - // This is only relevant for threads that aren't join()ed, as - // join() will take the `result` and set it to None, such that - // there is nothing left to drop here. - // If this panics, we should handle that, because we're outside the - // outermost `catch_unwind` of our thread. - // We just abort in that case, since there's nothing else we can do. - // (And even if we tried to handle it somehow, we'd also need to handle - // the case where the panic payload we get out of it also panics on - // drop, and so on. See issue #86027.) - if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| { - *self.result.get_mut() = None; - })) { - rtabort!("thread result panicked on drop"); - } - // Book-keeping so the scope knows when it's done. - if let Some(scope) = &self.scope { - // Now that there will be no more user code running on this thread - // that can use 'scope, mark the thread as 'finished'. - // It's important we only do this after the `result` has been dropped, - // since dropping it might still use things it borrowed from 'scope. - scope.decrement_num_running_threads(unhandled_panic); - } - } -} - -/// Inner representation for JoinHandle -struct JoinInner<'scope, T> { - native: imp::Thread, - thread: Thread, - packet: Arc>, -} - -impl<'scope, T> JoinInner<'scope, T> { - fn join(mut self) -> Result { - self.native.join(); - Arc::get_mut(&mut self.packet) - // FIXME(fuzzypixelz): returning an error instead of panicking here - // would require updating the documentation of - // `std::thread::Result`; currently we can return `Err` if and only - // if the thread had panicked. - .expect("threads should not terminate unexpectedly") - .result - .get_mut() - .take() - .unwrap() - } -} - -/// An owned permission to join on a thread (block on its termination). -/// -/// A `JoinHandle` *detaches* the associated thread when it is dropped, which -/// means that there is no longer any handle to the thread and no way to `join` -/// on it. -/// -/// Due to platform restrictions, it is not possible to [`Clone`] this -/// handle: the ability to join a thread is a uniquely-owned permission. -/// -/// This `struct` is created by the [`thread::spawn`] function and the -/// [`thread::Builder::spawn`] method. -/// -/// # Examples -/// -/// Creation from [`thread::spawn`]: -/// -/// ``` -/// use std::thread; -/// -/// let join_handle: thread::JoinHandle<_> = thread::spawn(|| { -/// // some work here -/// }); -/// ``` -/// -/// Creation from [`thread::Builder::spawn`]: -/// -/// ``` -/// use std::thread; -/// -/// let builder = thread::Builder::new(); -/// -/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { -/// // some work here -/// }).unwrap(); -/// ``` -/// -/// A thread being detached and outliving the thread that spawned it: -/// -/// ```no_run -/// use std::thread; -/// use std::time::Duration; -/// -/// let original_thread = thread::spawn(|| { -/// let _detached_thread = thread::spawn(|| { -/// // Here we sleep to make sure that the first thread returns before. -/// thread::sleep(Duration::from_millis(10)); -/// // This will be called, even though the JoinHandle is dropped. -/// println!("♫ Still alive ♫"); -/// }); -/// }); -/// -/// original_thread.join().expect("The thread being joined has panicked"); -/// println!("Original thread is joined."); -/// -/// // We make sure that the new thread has time to run, before the main -/// // thread returns. -/// -/// thread::sleep(Duration::from_millis(1000)); -/// ``` -/// -/// [`thread::Builder::spawn`]: Builder::spawn -/// [`thread::spawn`]: spawn -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(target_os = "teeos", must_use)] -pub struct JoinHandle(JoinInner<'static, T>); - -#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] -unsafe impl Send for JoinHandle {} -#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] -unsafe impl Sync for JoinHandle {} - -impl JoinHandle { - /// Extracts a handle to the underlying thread. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { - /// // some work here - /// }).unwrap(); - /// - /// let thread = join_handle.thread(); - /// println!("thread id: {:?}", thread.id()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - pub fn thread(&self) -> &Thread { - &self.0.thread - } - - /// Waits for the associated thread to finish. - /// - /// This function will return immediately if the associated thread has already finished. - /// - /// In terms of [atomic memory orderings], the completion of the associated - /// thread synchronizes with this function returning. In other words, all - /// operations performed by that thread [happen - /// before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses) all - /// operations that happen after `join` returns. - /// - /// If the associated thread panics, [`Err`] is returned with the parameter given - /// to [`panic!`] (though see the Notes below). - /// - /// [`Err`]: crate::result::Result::Err - /// [atomic memory orderings]: crate::sync::atomic - /// - /// # Panics - /// - /// This function may panic on some platforms if a thread attempts to join - /// itself or otherwise may create a deadlock with joining threads. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { - /// // some work here - /// }).unwrap(); - /// join_handle.join().expect("Couldn't join on the associated thread"); - /// ``` - /// - /// # Notes - /// - /// If a "foreign" unwinding operation (e.g. an exception thrown from C++ - /// code, or a `panic!` in Rust code compiled or linked with a different - /// runtime) unwinds all the way to the thread root, the process may be - /// aborted; see the Notes on [`thread::spawn`]. If the process is not - /// aborted, this function will return a `Result::Err` containing an opaque - /// type. - /// - /// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html - /// [`thread::spawn`]: spawn - #[stable(feature = "rust1", since = "1.0.0")] - pub fn join(self) -> Result { - self.0.join() - } - - /// Checks if the associated thread has finished running its main function. - /// - /// `is_finished` supports implementing a non-blocking join operation, by checking - /// `is_finished`, and calling `join` if it returns `true`. This function does not block. To - /// block while waiting on the thread to finish, use [`join`][Self::join]. - /// - /// This might return `true` for a brief moment after the thread's main - /// function has returned, but before the thread itself has stopped running. - /// However, once this returns `true`, [`join`][Self::join] can be expected - /// to return quickly, without blocking for any significant amount of time. - #[stable(feature = "thread_is_running", since = "1.61.0")] - pub fn is_finished(&self) -> bool { - Arc::strong_count(&self.0.packet) == 1 - } -} - -impl AsInner for JoinHandle { - fn as_inner(&self) -> &imp::Thread { - &self.0.native - } -} - -impl IntoInner for JoinHandle { - fn into_inner(self) -> imp::Thread { - self.0.native - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for JoinHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("JoinHandle").finish_non_exhaustive() - } -} - fn _assert_sync_and_send() { fn _assert_both() {} _assert_both::>(); _assert_both::(); } - -/// Returns an estimate of the default amount of parallelism a program should use. -/// -/// Parallelism is a resource. A given machine provides a certain capacity for -/// parallelism, i.e., a bound on the number of computations it can perform -/// simultaneously. This number often corresponds to the amount of CPUs a -/// computer has, but it may diverge in various cases. -/// -/// Host environments such as VMs or container orchestrators may want to -/// restrict the amount of parallelism made available to programs in them. This -/// is often done to limit the potential impact of (unintentionally) -/// resource-intensive programs on other programs running on the same machine. -/// -/// # Limitations -/// -/// The purpose of this API is to provide an easy and portable way to query -/// the default amount of parallelism the program should use. Among other things it -/// does not expose information on NUMA regions, does not account for -/// differences in (co)processor capabilities or current system load, -/// and will not modify the program's global state in order to more accurately -/// query the amount of available parallelism. -/// -/// Where both fixed steady-state and burst limits are available the steady-state -/// capacity will be used to ensure more predictable latencies. -/// -/// Resource limits can be changed during the runtime of a program, therefore the value is -/// not cached and instead recomputed every time this function is called. It should not be -/// called from hot code. -/// -/// The value returned by this function should be considered a simplified -/// approximation of the actual amount of parallelism available at any given -/// time. To get a more detailed or precise overview of the amount of -/// parallelism available to the program, you may wish to use -/// platform-specific APIs as well. The following platform limitations currently -/// apply to `available_parallelism`: -/// -/// On Windows: -/// - It may undercount the amount of parallelism available on systems with more -/// than 64 logical CPUs. However, programs typically need specific support to -/// take advantage of more than 64 logical CPUs, and in the absence of such -/// support, the number returned by this function accurately reflects the -/// number of logical CPUs the program can use by default. -/// - It may overcount the amount of parallelism available on systems limited by -/// process-wide affinity masks, or job object limitations. -/// -/// On Linux: -/// - It may overcount the amount of parallelism available when limited by a -/// process-wide affinity mask or cgroup quotas and `sched_getaffinity()` or cgroup fs can't be -/// queried, e.g. due to sandboxing. -/// - It may undercount the amount of parallelism if the current thread's affinity mask -/// does not reflect the process' cpuset, e.g. due to pinned threads. -/// - If the process is in a cgroup v1 cpu controller, this may need to -/// scan mountpoints to find the corresponding cgroup v1 controller, -/// which may take time on systems with large numbers of mountpoints. -/// (This does not apply to cgroup v2, or to processes not in a -/// cgroup.) -/// - It does not attempt to take `ulimit` into account. If there is a limit set on the number of -/// threads, `available_parallelism` cannot know how much of that limit a Rust program should -/// take, or know in a reliable and race-free way how much of that limit is already taken. -/// -/// On all targets: -/// - It may overcount the amount of parallelism available when running in a VM -/// with CPU usage limits (e.g. an overcommitted host). -/// -/// # Errors -/// -/// This function will, but is not limited to, return errors in the following -/// cases: -/// -/// - If the amount of parallelism is not known for the target platform. -/// - If the program lacks permission to query the amount of parallelism made -/// available to it. -/// -/// # Examples -/// -/// ``` -/// # #![allow(dead_code)] -/// use std::{io, thread}; -/// -/// fn main() -> io::Result<()> { -/// let count = thread::available_parallelism()?.get(); -/// assert!(count >= 1_usize); -/// Ok(()) -/// } -/// ``` -#[doc(alias = "available_concurrency")] // Alias for a previous name we gave this API on unstable. -#[doc(alias = "hardware_concurrency")] // Alias for C++ `std::thread::hardware_concurrency`. -#[doc(alias = "num_cpus")] // Alias for a popular ecosystem crate which provides similar functionality. -#[stable(feature = "available_parallelism", since = "1.59.0")] -pub fn available_parallelism() -> io::Result> { - imp::available_parallelism() -} diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 75a5303fc321a..301f5e949cac3 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -1,4 +1,8 @@ -use super::{Builder, JoinInner, Result, Thread, current_or_unnamed}; +use super::Result; +use super::builder::Builder; +use super::current::current_or_unnamed; +use super::lifecycle::{JoinInner, spawn_unchecked}; +use super::thread::Thread; use crate::marker::PhantomData; use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind}; use crate::sync::Arc; @@ -257,7 +261,10 @@ impl Builder { F: FnOnce() -> T + Send + 'scope, T: Send + 'scope, { - Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(f, Some(scope.data.clone())) }?)) + let Builder { name, stack_size, no_hooks } = self; + Ok(ScopedJoinHandle(unsafe { + spawn_unchecked(name, stack_size, no_hooks, Some(scope.data.clone()), f) + }?)) } } @@ -279,7 +286,7 @@ impl<'scope, T> ScopedJoinHandle<'scope, T> { #[must_use] #[stable(feature = "scoped_threads", since = "1.63.0")] pub fn thread(&self) -> &Thread { - &self.0.thread + self.0.thread() } /// Waits for the associated thread to finish. @@ -325,7 +332,7 @@ impl<'scope, T> ScopedJoinHandle<'scope, T> { /// to return quickly, without blocking for any significant amount of time. #[stable(feature = "scoped_threads", since = "1.63.0")] pub fn is_finished(&self) -> bool { - Arc::strong_count(&self.0.packet) == 1 + self.0.is_finished() } } diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index c8a7bcf55c14e..254793ac33d08 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -1,7 +1,7 @@ +use super::thread::Thread; use crate::cell::Cell; use crate::iter; use crate::sync::Arc; -use crate::thread::Thread; crate::thread_local! { /// A thread local linked list of spawn hooks. diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index 2117f5f93ce26..4b934c039a36f 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -1,11 +1,10 @@ -use super::Builder; use crate::any::Any; use crate::panic::panic_any; use crate::result; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::mpsc::{Sender, channel}; use crate::sync::{Arc, Barrier}; -use crate::thread::{self, Scope, ThreadId}; +use crate::thread::{self, Builder, Scope, ThreadId}; use crate::time::{Duration, Instant}; // !!! These tests are dangerous. If something is buggy, they will hang, !!! diff --git a/library/std/src/thread/thread.rs b/library/std/src/thread/thread.rs new file mode 100644 index 0000000000000..63ce0540bd477 --- /dev/null +++ b/library/std/src/thread/thread.rs @@ -0,0 +1,320 @@ +use super::id::ThreadId; +use super::main_thread; +use crate::ffi::CStr; +use crate::fmt; +use crate::pin::Pin; +use crate::sync::Arc; +use crate::sys::sync::Parker; +use crate::time::Duration; + +// This module ensures private fields are kept private, which is necessary to enforce the safety requirements. +mod thread_name_string { + use crate::ffi::{CStr, CString}; + use crate::str; + + /// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated. + pub(crate) struct ThreadNameString { + inner: CString, + } + + impl From for ThreadNameString { + fn from(s: String) -> Self { + Self { + inner: CString::new(s).expect("thread name may not contain interior null bytes"), + } + } + } + + impl ThreadNameString { + pub fn as_cstr(&self) -> &CStr { + &self.inner + } + + pub fn as_str(&self) -> &str { + // SAFETY: `ThreadNameString` is guaranteed to be UTF-8. + unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) } + } + } +} + +use thread_name_string::ThreadNameString; + +/// The internal representation of a `Thread` handle +/// +/// We explicitly set the alignment for our guarantee in Thread::into_raw. This +/// allows applications to stuff extra metadata bits into the alignment, which +/// can be rather useful when working with atomics. +#[repr(align(8))] +struct Inner { + name: Option, + id: ThreadId, + parker: Parker, +} + +impl Inner { + fn parker(self: Pin<&Self>) -> Pin<&Parker> { + unsafe { Pin::map_unchecked(self, |inner| &inner.parker) } + } +} + +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +/// A handle to a thread. +/// +/// Threads are represented via the `Thread` type, which you can get in one of +/// two ways: +/// +/// * By spawning a new thread, e.g., using the [`thread::spawn`] +/// function, and calling [`thread`] on the [`JoinHandle`]. +/// * By requesting the current thread, using the [`thread::current`] function. +/// +/// The [`thread::current`] function is available even for threads not spawned +/// by the APIs of this module. +/// +/// There is usually no need to create a `Thread` struct yourself, one +/// should instead use a function like `spawn` to create new threads, see the +/// docs of [`Builder`] and [`spawn`] for more details. +/// +/// [`thread::spawn`]: super::spawn +/// [`thread`]: super::JoinHandle::thread +/// [`JoinHandle`]: super::JoinHandle +/// [`thread::current`]: super::current::current +/// [`Builder`]: super::Builder +/// [`spawn`]: super::spawn +pub struct Thread { + inner: Pin>, +} + +impl Thread { + pub(crate) fn new(id: ThreadId, name: Option) -> Thread { + let name = name.map(ThreadNameString::from); + + // We have to use `unsafe` here to construct the `Parker` in-place, + // which is required for the UNIX implementation. + // + // SAFETY: We pin the Arc immediately after creation, so its address never + // changes. + let inner = unsafe { + let mut arc = Arc::::new_uninit(); + let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr(); + (&raw mut (*ptr).name).write(name); + (&raw mut (*ptr).id).write(id); + Parker::new_in_place(&raw mut (*ptr).parker); + Pin::new_unchecked(arc.assume_init()) + }; + + Thread { inner } + } + + /// Like the public [`park`], but callable on any handle. This is used to + /// allow parking in TLS destructors. + /// + /// # Safety + /// May only be called from the thread to which this handle belongs. + /// + /// [`park`]: super::park + pub(crate) unsafe fn park(&self) { + unsafe { self.inner.as_ref().parker().park() } + } + + /// Like the public [`park_timeout`], but callable on any handle. This is + /// used to allow parking in TLS destructors. + /// + /// # Safety + /// May only be called from the thread to which this handle belongs. + /// + /// [`park_timeout`]: super::park_timeout + pub(crate) unsafe fn park_timeout(&self, dur: Duration) { + unsafe { self.inner.as_ref().parker().park_timeout(dur) } + } + + /// Atomically makes the handle's token available if it is not already. + /// + /// Every thread is equipped with some basic low-level blocking support, via + /// the [`park`] function and the `unpark()` method. These can be used as a + /// more CPU-efficient implementation of a spinlock. + /// + /// See the [park documentation] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// use std::time::Duration; + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// static QUEUED: AtomicBool = AtomicBool::new(false); + /// + /// let parked_thread = thread::Builder::new() + /// .spawn(|| { + /// println!("Parking thread"); + /// QUEUED.store(true, Ordering::Release); + /// thread::park(); + /// println!("Thread unparked"); + /// }) + /// .unwrap(); + /// + /// // Let some time pass for the thread to be spawned. + /// thread::sleep(Duration::from_millis(10)); + /// + /// // Wait until the other thread is queued. + /// // This is crucial! It guarantees that the `unpark` below is not consumed + /// // by some other code in the parked thread (e.g. inside `println!`). + /// while !QUEUED.load(Ordering::Acquire) { + /// // Spinning is of course inefficient; in practice, this would more likely be + /// // a dequeue where we have no work to do if there's nobody queued. + /// std::hint::spin_loop(); + /// } + /// + /// println!("Unpark the thread"); + /// parked_thread.thread().unpark(); + /// + /// parked_thread.join().unwrap(); + /// ``` + /// + /// [`park`]: super::park + /// [park documentation]: super::park + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn unpark(&self) { + self.inner.as_ref().parker().unpark(); + } + + /// Gets the thread's unique identifier. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let other_thread = thread::spawn(|| { + /// thread::current().id() + /// }); + /// + /// let other_thread_id = other_thread.join().unwrap(); + /// assert!(thread::current().id() != other_thread_id); + /// ``` + #[stable(feature = "thread_id", since = "1.19.0")] + #[must_use] + pub fn id(&self) -> ThreadId { + self.inner.id + } + + /// Gets the thread's name. + /// + /// For more information about named threads, see + /// [this module-level documentation][naming-threads]. + /// + /// # Examples + /// + /// Threads by default have no name specified: + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let handler = builder.spawn(|| { + /// assert!(thread::current().name().is_none()); + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// Thread with a specified name: + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()); + /// + /// let handler = builder.spawn(|| { + /// assert_eq!(thread::current().name(), Some("foo")) + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// [naming-threads]: ./index.html#naming-threads + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub fn name(&self) -> Option<&str> { + if let Some(name) = &self.inner.name { + Some(name.as_str()) + } else if main_thread::get() == Some(self.inner.id) { + Some("main") + } else { + None + } + } + + /// Consumes the `Thread`, returning a raw pointer. + /// + /// To avoid a memory leak the pointer must be converted + /// back into a `Thread` using [`Thread::from_raw`]. The pointer is + /// guaranteed to be aligned to at least 8 bytes. + /// + /// # Examples + /// + /// ``` + /// #![feature(thread_raw)] + /// + /// use std::thread::{self, Thread}; + /// + /// let thread = thread::current(); + /// let id = thread.id(); + /// let ptr = Thread::into_raw(thread); + /// unsafe { + /// assert_eq!(Thread::from_raw(ptr).id(), id); + /// } + /// ``` + #[unstable(feature = "thread_raw", issue = "97523")] + pub fn into_raw(self) -> *const () { + // Safety: We only expose an opaque pointer, which maintains the `Pin` invariant. + let inner = unsafe { Pin::into_inner_unchecked(self.inner) }; + Arc::into_raw(inner) as *const () + } + + /// Constructs a `Thread` from a raw pointer. + /// + /// The raw pointer must have been previously returned + /// by a call to [`Thread::into_raw`]. + /// + /// # Safety + /// + /// This function is unsafe because improper use may lead + /// to memory unsafety, even if the returned `Thread` is never + /// accessed. + /// + /// Creating a `Thread` from a pointer other than one returned + /// from [`Thread::into_raw`] is **undefined behavior**. + /// + /// Calling this function twice on the same raw pointer can lead + /// to a double-free if both `Thread` instances are dropped. + #[unstable(feature = "thread_raw", issue = "97523")] + pub unsafe fn from_raw(ptr: *const ()) -> Thread { + // Safety: Upheld by caller. + unsafe { Thread { inner: Pin::new_unchecked(Arc::from_raw(ptr as *const Inner)) } } + } + + pub(crate) fn cname(&self) -> Option<&CStr> { + if let Some(name) = &self.inner.name { + Some(name.as_cstr()) + } else if main_thread::get() == Some(self.inner.id) { + Some(c"main") + } else { + None + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Thread { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Thread") + .field("id", &self.id()) + .field("name", &self.name()) + .finish_non_exhaustive() + } +} diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 7e1b59351e042..a591be05291fa 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -206,7 +206,6 @@ pub(crate) fn is_ci_llvm_available_for_target( ("aarch64-unknown-linux-gnu", false), ("aarch64-apple-darwin", false), ("aarch64-pc-windows-msvc", false), - ("i686-pc-windows-gnu", false), ("i686-pc-windows-msvc", false), ("i686-unknown-linux-gnu", false), ("x86_64-unknown-linux-gnu", true), @@ -215,9 +214,11 @@ pub(crate) fn is_ci_llvm_available_for_target( ("x86_64-pc-windows-msvc", true), // tier 2 with host tools ("aarch64-unknown-linux-musl", false), + ("aarch64-pc-windows-gnullvm", false), ("arm-unknown-linux-gnueabi", false), ("arm-unknown-linux-gnueabihf", false), ("armv7-unknown-linux-gnueabihf", false), + ("i686-pc-windows-gnu", false), ("loongarch64-unknown-linux-gnu", false), ("loongarch64-unknown-linux-musl", false), ("powerpc-unknown-linux-gnu", false), @@ -226,6 +227,7 @@ pub(crate) fn is_ci_llvm_available_for_target( ("powerpc64le-unknown-linux-musl", false), ("riscv64gc-unknown-linux-gnu", false), ("s390x-unknown-linux-gnu", false), + ("x86_64-pc-windows-gnullvm", true), ("x86_64-unknown-freebsd", false), ("x86_64-unknown-illumos", false), ("x86_64-unknown-linux-musl", false), diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 535e6a510ca68..c5006912c97ac 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -228,9 +228,8 @@ pub fn prepare_tool_cargo( // own copy cargo.env("LZMA_API_STATIC", "1"); - // Note that `miri` always uses jemalloc. As such, there is no checking of the jemalloc build flag. // See also the "JEMALLOC_SYS_WITH_LG_PAGE" setting in the compile build step. - if env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() { + if builder.config.jemalloc(target) && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() { // Build jemalloc on AArch64 with support for page sizes up to 64K // See: https://github.com/rust-lang/rust/pull/135081 if target.starts_with("aarch64") { diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index a096d116e73fa..d950dc1a1c582 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -447,6 +447,7 @@ pub(crate) fn is_download_ci_available(target_triple: &str, llvm_assertions: boo // All tier 1 targets and tier 2 targets with host tools. const SUPPORTED_PLATFORMS: &[&str] = &[ "aarch64-apple-darwin", + "aarch64-pc-windows-gnullvm", "aarch64-pc-windows-msvc", "aarch64-unknown-linux-gnu", "aarch64-unknown-linux-musl", @@ -465,6 +466,7 @@ pub(crate) fn is_download_ci_available(target_triple: &str, llvm_assertions: boo "s390x-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-gnu", + "x86_64-pc-windows-gnullvm", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", "x86_64-unknown-illumos", diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index ed280addb3b84..37ad5f09897fa 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -599,6 +599,6 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ ChangeInfo { change_id: 148795, severity: ChangeSeverity::Info, - summary: "New options `rust.rustflags` for all targets and `rustflags` par target that will pass specified flags to rustc for all stages. Target specific flags override global `rust.rustflags` ones.", + summary: "New options `rust.rustflags` for all targets and per-target `rustflags` that will pass specified flags to rustc for all stages. Target-specific flags override global `rust.rustflags` ones.", }, ]; diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index eee13ff2b0dc0..2b9562f26574a 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1126,7 +1126,8 @@ pub(crate) fn print_impl( } if impl_.kind.is_fake_variadic() && let Some(generics) = ty.generics() - && let Ok(inner_type) = generics.exactly_one() + // FIXME: rewrite in terms of `#![feature(exact_length_collection)]`. See: #149266 + && let Ok(inner_type) = Itertools::exactly_one(generics) { let last = ty.last(); if f.alternate() { @@ -1206,7 +1207,8 @@ impl clean::Impl { } } else if let clean::Type::Path { path } = type_ && let Some(generics) = path.generics() - && let Ok(ty) = generics.exactly_one() + // FIXME: rewrite in terms of `#![feature(exact_length_collection)]`. See: #149266 + && let Ok(ty) = Itertools::exactly_one(generics) && self.kind.is_fake_variadic() { print_anchor(path.def_id(), path.last(), cx).fmt(f)?; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 2b381d64547b5..73bc28e4bc7d0 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -5050,9 +5050,11 @@ ${obj.displayPath}${name}\ if (query.proposeCorrectionFrom !== null && isTypeSearch) { const orig = query.proposeCorrectionFrom; const targ = query.proposeCorrectionTo; - correctionOutput = "

" + - `Type "${orig}" not found and used as generic parameter. ` + - `Consider searching for "${targ}" instead.

`; + let message = `Type "${orig}" not found and used as generic parameter.`; + if (targ !== null) { + message += ` Consider searching for "${targ}" instead.`; + } + correctionOutput = `

${message}

`; } if (firstResult.value) { if (correctionOutput !== "") { diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 0d783fde33131..08e7c7593cb27 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -92,7 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { (matches!(v.data, VariantData::Unit(_, _)) && is_doc_hidden(cx.tcx.hir_attrs(v.hir_id))) .then_some((v.def_id, v.span)) }); - if let Ok((id, span)) = iter.exactly_one() + // FIXME: rewrite in terms of `#![feature(exact_length_collection)]`. See: #149266 + if let Ok((id, span)) = Itertools::exactly_one(iter) && !find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::NonExhaustive(..)) { self.potential_enums.push((item.owner_id.def_id, id, item.span, span)); @@ -104,7 +105,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { .iter() .filter(|field| !cx.effective_visibilities.is_exported(field.def_id)); if fields.len() > 1 - && let Ok(field) = private_fields.exactly_one() + // FIXME: rewrite in terms of `#![feature(exact_length_collection)]`. See: #149266 + && let Ok(field) = Itertools::exactly_one(private_fields) && let TyKind::Tup([]) = field.ty.kind { span_lint_and_then( diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr index 582c6f11e3602..c80cbf835a386 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr @@ -12,8 +12,8 @@ LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC - = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC - = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::lifecycle::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/join_handle.rs:LL:CC note: inside `main` --> tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC | diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr index d8d0ff37e4100..f174d387f76e3 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr @@ -12,8 +12,8 @@ LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC - = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC - = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::lifecycle::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/join_handle.rs:LL:CC note: inside `main` --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC | diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr index c9a4004ebfb31..06b8e1246e016 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr @@ -12,8 +12,8 @@ LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC - = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC - = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::lifecycle::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/join_handle.rs:LL:CC note: inside `main` --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC | diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr index 47a0ebdcfef82..52affb767db51 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr @@ -8,8 +8,8 @@ LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle( = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC - = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC - = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::lifecycle::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/join_handle.rs:LL:CC note: inside `main` --> tests/fail-dep/concurrency/windows_join_detached.rs:LL:CC | diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr index 93f800ecca331..0ab89676db370 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr @@ -14,8 +14,8 @@ LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle( | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC - = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC - = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::lifecycle::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/join_handle.rs:LL:CC note: inside `main` --> tests/fail-dep/concurrency/windows_join_main.rs:LL:CC | diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr index c76da9151b35e..bdfab966d6d98 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr @@ -12,8 +12,8 @@ LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle( | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC - = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC - = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::lifecycle::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/join_handle.rs:LL:CC note: inside `main` --> tests/fail-dep/concurrency/windows_join_self.rs:LL:CC | diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr index a7dfa0b6ea650..6253fe6d2c768 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr @@ -6,8 +6,8 @@ LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC - = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC - = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::lifecycle::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/join_handle.rs:LL:CC note: inside `main` --> tests/fail-dep/libc/eventfd_block_read_twice.rs:LL:CC | diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr index 0e554598ecf56..aecc54c2fd882 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr @@ -6,8 +6,8 @@ LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC - = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC - = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::lifecycle::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/join_handle.rs:LL:CC note: inside `main` --> tests/fail-dep/libc/eventfd_block_write_twice.rs:LL:CC | diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr index 6740faedb3e9d..14390d632738d 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr @@ -6,8 +6,8 @@ LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC - = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC - = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::lifecycle::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/join_handle.rs:LL:CC note: inside `main` --> tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC | diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr index 1e802209fd795..97b5df7fb17ff 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr @@ -6,8 +6,8 @@ LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC - = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC - = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::lifecycle::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/join_handle.rs:LL:CC note: inside `main` --> tests/fail-dep/libc/socketpair-close-while-blocked.rs:LL:CC | diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr index 3f7bbc779609f..38db735e9b4d4 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr @@ -6,8 +6,8 @@ LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC - = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC - = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::lifecycle::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/join_handle.rs:LL:CC note: inside `main` --> tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC | diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr index b8dbb513da209..a2f1c67b5efc8 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr @@ -6,8 +6,8 @@ LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC - = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC - = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::lifecycle::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/join_handle.rs:LL:CC note: inside `main` --> tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC | diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 6fe63f249cd4a..78fac8ff13517 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -786,7 +786,8 @@ fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> { && let DelimiterKind::Parenthesis | DelimiterKind::Invisible = sub.delimiter.kind { tt = - tt_iter.exactly_one().map_err(|_| sub.delimiter.open.cover(sub.delimiter.close))?; + // FIXME: rewrite in terms of `#![feature(exact_length_collection)]`. See: #149266 + Itertools::exactly_one(tt_iter).map_err(|_| sub.delimiter.open.cover(sub.delimiter.close))?; } match tt { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index 9d5d3f223707a..91cee59ad8d00 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -163,7 +163,8 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> let name_ref = ctx.find_node_at_offset::()?; let mcall = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?; let receiver = mcall.receiver()?; - let closure_body = mcall.arg_list()?.args().exactly_one().ok()?; + // FIXME: rewrite in terms of `#![feature(exact_length_collection)]`. See: #149266 + let closure_body = Itertools::exactly_one(mcall.arg_list()?.args()).ok()?; let closure_body = match closure_body { ast::Expr::ClosureExpr(expr) => expr.body()?, _ => return None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs index 68cb764030956..ba577b217df78 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs @@ -113,7 +113,7 @@ fn extract_range(iterable: &ast::Expr) -> Option<(ast::Expr, Option, (range.start()?, range.end(), make::expr_literal("1").into(), inclusive) } ast::Expr::MethodCallExpr(call) if call.name_ref()?.text() == "step_by" => { - let [step] = call.arg_list()?.args().collect_array()?; + let [step] = Itertools::collect_array(call.arg_list()?.args())?; let (start, end, _, inclusive) = extract_range(&call.receiver()?)?; (start, end, step, inclusive) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs index 00977ea4e533b..66b16681f47af 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs @@ -8,9 +8,8 @@ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let completions = completion_list_with_config_raw(TEST_CONFIG, ra_fixture, true, None); let (db, position) = position(ra_fixture); let mut actual = db.file_text(position.file_id).text(&db).to_string(); - completions - .into_iter() - .exactly_one() + // FIXME: rewrite in terms of `#![feature(exact_length_collection)]`. See: #149266 + Itertools::exactly_one(completions.into_iter()) .expect("more than one completion") .text_edit .apply(&mut actual); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index 3464a9644b19d..b1b428e7068dc 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -113,7 +113,8 @@ impl Project<'_> { let mut buf = Vec::new(); flags::Lsif::run( flags::Lsif { - path: tmp_dir_path.join(self.roots.iter().exactly_one().unwrap()).into(), + // FIXME: rewrite in terms of `#![feature(exact_length_collection)]`. See: #149266 + path: tmp_dir_path.join(Itertools::exactly_one(self.roots.iter()).unwrap()).into(), exclude_vendored_libraries: false, }, &mut buf, diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index 23f30e8dab3b3..111fe89e7eb0a 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -445,6 +445,7 @@ pub fn check(path: &Path, tidy_ctx: TidyCtx) { let mut comment_block: Option<(usize, usize)> = None; let is_test = file.components().any(|c| c.as_os_str() == "tests") || file.file_stem().unwrap() == "tests"; + let is_codegen_test = is_test && file.components().any(|c| c.as_os_str() == "codegen-llvm"); let is_this_file = file.ends_with(this_file) || this_file.ends_with(file); let is_test_for_this_file = is_test && file.parent().unwrap().ends_with(this_file.with_extension("")); @@ -488,6 +489,11 @@ pub fn check(path: &Path, tidy_ctx: TidyCtx) { ) } + if is_codegen_test && trimmed.contains("CHECK") && trimmed.ends_with(": br") { + err("`CHECK: br` and `CHECK-NOT: br` in codegen tests are fragile to false \ + positives in mangled symbols. Try using `br {{.*}}` instead.") + } + if !under_rustfmt && line.chars().count() > max_columns && !long_line_is_ok(&extension, is_error_code, max_columns, line) diff --git a/tests/codegen-llvm/char-ascii-branchless.rs b/tests/codegen-llvm/char-ascii-branchless.rs index f99066aa9aaec..2cd5b5135a407 100644 --- a/tests/codegen-llvm/char-ascii-branchless.rs +++ b/tests/codegen-llvm/char-ascii-branchless.rs @@ -7,41 +7,41 @@ // CHECK-LABEL: @is_ascii_alphanumeric_char #[no_mangle] pub fn is_ascii_alphanumeric_char(x: char) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.is_ascii_alphanumeric() } // CHECK-LABEL: @is_ascii_alphanumeric_u8 #[no_mangle] pub fn is_ascii_alphanumeric_u8(x: u8) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.is_ascii_alphanumeric() } // CHECK-LABEL: @is_ascii_hexdigit_char #[no_mangle] pub fn is_ascii_hexdigit_char(x: char) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.is_ascii_hexdigit() } // CHECK-LABEL: @is_ascii_hexdigit_u8 #[no_mangle] pub fn is_ascii_hexdigit_u8(x: u8) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.is_ascii_hexdigit() } // CHECK-LABEL: @is_ascii_punctuation_char #[no_mangle] pub fn is_ascii_punctuation_char(x: char) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.is_ascii_punctuation() } // CHECK-LABEL: @is_ascii_punctuation_u8 #[no_mangle] pub fn is_ascii_punctuation_u8(x: u8) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.is_ascii_punctuation() } diff --git a/tests/codegen-llvm/enum/enum-early-otherwise-branch.rs b/tests/codegen-llvm/enum/enum-early-otherwise-branch.rs index 8d39d8e9b7466..4cadef3b6988a 100644 --- a/tests/codegen-llvm/enum/enum-early-otherwise-branch.rs +++ b/tests/codegen-llvm/enum/enum-early-otherwise-branch.rs @@ -12,7 +12,7 @@ pub enum Enum { pub fn foo(lhs: &Enum, rhs: &Enum) -> bool { // CHECK-LABEL: define{{.*}}i1 @foo( // CHECK-NOT: switch - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: [[SELECT:%.*]] = select // CHECK-NEXT: ret i1 [[SELECT]] // CHECK-NEXT: } diff --git a/tests/codegen-llvm/issues/cows-dont-have-branches-117763.rs b/tests/codegen-llvm/issues/cows-dont-have-branches-117763.rs index b97729fa14657..c143f7aeabfa4 100644 --- a/tests/codegen-llvm/issues/cows-dont-have-branches-117763.rs +++ b/tests/codegen-llvm/issues/cows-dont-have-branches-117763.rs @@ -9,7 +9,7 @@ // CHECK-LABEL: @branchless_cow_slices #[no_mangle] pub fn branchless_cow_slices<'a>(cow: &'a std::borrow::Cow<'a, [u8]>) -> &'a [u8] { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: select // CHECK-NOT: icmp // CHECK: ret { ptr, {{i32|i64}} } diff --git a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs index 69aefc6b1fb54..b8b9ea7436f33 100644 --- a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs +++ b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs @@ -11,7 +11,7 @@ use std::slice::Iter; #[no_mangle] pub unsafe fn foo(x: &mut Copied>) -> u32 { // CHECK-LABEL: @foo( - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: select // CHECK: [[RET:%.*]] = load i32, ptr // CHECK-NEXT: ret i32 [[RET]] diff --git a/tests/codegen-llvm/issues/issue-108395-branchy-bool-match.rs b/tests/codegen-llvm/issues/issue-108395-branchy-bool-match.rs index 96387e791b03a..86bbbadcd9062 100644 --- a/tests/codegen-llvm/issues/issue-108395-branchy-bool-match.rs +++ b/tests/codegen-llvm/issues/issue-108395-branchy-bool-match.rs @@ -6,7 +6,7 @@ // CHECK-LABEL: @wildcard( #[no_mangle] pub fn wildcard(a: u16, b: u16, v: u16) -> u16 { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} match (a == v, b == v) { (true, false) => 0, (false, true) => u16::MAX, @@ -17,7 +17,7 @@ pub fn wildcard(a: u16, b: u16, v: u16) -> u16 { // CHECK-LABEL: @exhaustive( #[no_mangle] pub fn exhaustive(a: u16, b: u16, v: u16) -> u16 { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} match (a == v, b == v) { (true, false) => 0, (false, true) => u16::MAX, diff --git a/tests/codegen-llvm/issues/issue-119422.rs b/tests/codegen-llvm/issues/issue-119422.rs index 17ae71605b58d..2c553fd1fa876 100644 --- a/tests/codegen-llvm/issues/issue-119422.rs +++ b/tests/codegen-llvm/issues/issue-119422.rs @@ -19,45 +19,45 @@ pub fn check_non_null(x: NonNull) -> bool { // CHECK-LABEL: @equals_zero_is_false_u8 #[no_mangle] pub fn equals_zero_is_false_u8(x: NonZero) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i1 false - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.get() == 0 } // CHECK-LABEL: @not_equals_zero_is_true_u8 #[no_mangle] pub fn not_equals_zero_is_true_u8(x: NonZero) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i1 true - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.get() != 0 } // CHECK-LABEL: @equals_zero_is_false_i8 #[no_mangle] pub fn equals_zero_is_false_i8(x: NonZero) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i1 false - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.get() == 0 } // CHECK-LABEL: @not_equals_zero_is_true_i8 #[no_mangle] pub fn not_equals_zero_is_true_i8(x: NonZero) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i1 true - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.get() != 0 } // CHECK-LABEL: @usize_try_from_u32 #[no_mangle] pub fn usize_try_from_u32(x: NonZero) -> NonZero { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: zext i32 %{{.*}} to i64 - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i64 x.try_into().unwrap() } @@ -65,9 +65,9 @@ pub fn usize_try_from_u32(x: NonZero) -> NonZero { // CHECK-LABEL: @isize_try_from_i32 #[no_mangle] pub fn isize_try_from_i32(x: NonZero) -> NonZero { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: sext i32 %{{.*}} to i64 - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i64 x.try_into().unwrap() } @@ -75,9 +75,9 @@ pub fn isize_try_from_i32(x: NonZero) -> NonZero { // CHECK-LABEL: @u64_from_nonzero_is_not_zero #[no_mangle] pub fn u64_from_nonzero_is_not_zero(x: NonZero) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i1 false - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} let v: u64 = x.into(); v == 0 } diff --git a/tests/codegen-llvm/issues/issue-73258.rs b/tests/codegen-llvm/issues/issue-73258.rs index 936a75544966b..c9eceb0dccf73 100644 --- a/tests/codegen-llvm/issues/issue-73258.rs +++ b/tests/codegen-llvm/issues/issue-73258.rs @@ -18,7 +18,7 @@ pub enum Foo { pub unsafe fn issue_73258(ptr: *const Foo) -> Foo { // CHECK-NOT: icmp // CHECK-NOT: call - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: select // CHECK: %[[R:.+]] = load i8 @@ -26,14 +26,14 @@ pub unsafe fn issue_73258(ptr: *const Foo) -> Foo { // CHECK-NOT: icmp // CHECK-NOT: call - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: select // CHECK: ret i8 %[[R]] // CHECK-NOT: icmp // CHECK-NOT: call - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: select let k: Option = Some(ptr.read()); return k.unwrap(); diff --git a/tests/codegen-llvm/issues/num-is-digit-to-digit-59352.rs b/tests/codegen-llvm/issues/num-is-digit-to-digit-59352.rs index 1d23eb2cdf9e4..4f5eea5f7e1c4 100644 --- a/tests/codegen-llvm/issues/num-is-digit-to-digit-59352.rs +++ b/tests/codegen-llvm/issues/num-is-digit-to-digit-59352.rs @@ -8,7 +8,7 @@ // CHECK-LABEL: @num_to_digit_slow #[no_mangle] pub fn num_to_digit_slow(num: char) -> u32 { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: panic if num.is_digit(8) { num.to_digit(8).unwrap() } else { 0 } } diff --git a/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs b/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs index 6f34097842800..1da7de535f75c 100644 --- a/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs +++ b/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs @@ -19,23 +19,23 @@ impl Drop for NotCopy { // CHECK-LABEL: @iter_repeat_n_next pub fn iter_repeat_n_next(it: &mut std::iter::RepeatN) -> Option { // CHECK-NEXT: start: - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: %[[COUNT:.+]] = load i64 // CHECK-NEXT: %[[COUNT_ZERO:.+]] = icmp eq i64 %[[COUNT]], 0 // CHECK-NEXT: br i1 %[[COUNT_ZERO]], label %[[EMPTY:.+]], label %[[NOT_EMPTY:.+]] // CHECK: [[NOT_EMPTY]]: - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: %[[DEC:.+]] = add i64 %[[COUNT]], -1 // CHECK-NEXT: %[[VAL:.+]] = load i16 // CHECK-NEXT: store i64 %[[DEC]] // CHECK-NEXT: br label %[[EMPTY]] // CHECK: [[EMPTY]]: - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: phi i16 // CHECK-SAME: [ %[[VAL]], %[[NOT_EMPTY]] ] - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret it.next() diff --git a/tests/codegen-llvm/option-as-slice.rs b/tests/codegen-llvm/option-as-slice.rs index 39b34a2035b74..79cdd613aaadc 100644 --- a/tests/codegen-llvm/option-as-slice.rs +++ b/tests/codegen-llvm/option-as-slice.rs @@ -11,14 +11,14 @@ use core::option::Option; #[no_mangle] pub fn u64_opt_as_slice(o: &Option) -> &[u64] { // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: %[[LEN:.+]] = load i64 // CHECK-SAME: !range ![[META_U64:[0-9]+]], // CHECK-SAME: !noundef // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: %[[T0:.+]] = insertvalue { ptr, i64 } poison, ptr %{{.+}}, 0 @@ -31,13 +31,13 @@ pub fn u64_opt_as_slice(o: &Option) -> &[u64] { #[no_mangle] pub fn nonzero_u64_opt_as_slice(o: &Option>) -> &[NonZero] { // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: %[[NZ:.+]] = icmp ne i64 %{{.+}}, 0 // CHECK-NEXT: %[[LEN:.+]] = zext i1 %[[NZ]] to i64 // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: %[[T0:.+]] = insertvalue { ptr, i64 } poison, ptr %o, 0 @@ -50,7 +50,7 @@ pub fn nonzero_u64_opt_as_slice(o: &Option>) -> &[NonZero] { #[no_mangle] pub fn u8_opt_as_slice(o: &Option) -> &[u8] { // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: %[[TAG:.+]] = load i8 @@ -58,7 +58,7 @@ pub fn u8_opt_as_slice(o: &Option) -> &[u8] { // CHECK-SAME: !noundef // CHECK: %[[LEN:.+]] = zext{{.*}} i8 %[[TAG]] to i64 // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: %[[T0:.+]] = insertvalue { ptr, i64 } poison, ptr %{{.+}}, 0 diff --git a/tests/codegen-llvm/slice-init.rs b/tests/codegen-llvm/slice-init.rs index 950e0b0c10db7..12e9df24565e1 100644 --- a/tests/codegen-llvm/slice-init.rs +++ b/tests/codegen-llvm/slice-init.rs @@ -69,7 +69,7 @@ const N: usize = 100; #[no_mangle] pub fn u16_init_one_bytes() -> [u16; N] { // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: call void @llvm.memset.p0 diff --git a/tests/codegen-llvm/slice-iter-fold.rs b/tests/codegen-llvm/slice-iter-fold.rs index 55ab34661c36e..c63b5f8f7acdd 100644 --- a/tests/codegen-llvm/slice-iter-fold.rs +++ b/tests/codegen-llvm/slice-iter-fold.rs @@ -5,7 +5,7 @@ #[no_mangle] pub fn slice_fold_to_last(slice: &[i32]) -> Option<&i32> { // CHECK-NOT: loop - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: call // CHECK: ret slice.iter().fold(None, |_, i| Some(i)) diff --git a/tests/codegen-llvm/vecdeque-drain.rs b/tests/codegen-llvm/vecdeque-drain.rs index a5e5da6501332..5709ead8bf939 100644 --- a/tests/codegen-llvm/vecdeque-drain.rs +++ b/tests/codegen-llvm/vecdeque-drain.rs @@ -10,7 +10,7 @@ use std::collections::VecDeque; // CHECK-LABEL: @clear // CHECK-NOT: call -// CHECK-NOT: br +// CHECK-NOT: br {{.*}} // CHECK: getelementptr inbounds // CHECK-NEXT: {{call void @llvm.memset|store}} // CHECK-NEXT: ret void @@ -21,15 +21,15 @@ pub fn clear(v: &mut VecDeque) { // CHECK-LABEL: @truncate // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK-NOT: br +// CHECK-NOT: br {{.*}} // CHECK: ret void #[no_mangle] pub fn truncate(v: &mut VecDeque, n: usize) { @@ -40,17 +40,17 @@ pub fn truncate(v: &mut VecDeque, n: usize) { // CHECK-LABEL: @advance // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK-NOT: br +// CHECK-NOT: br {{.*}} // CHECK: ret void #[no_mangle] pub fn advance(v: &mut VecDeque, n: usize) { diff --git a/tests/rustdoc-js/alias-sort.js b/tests/rustdoc-js/alias-sort.js new file mode 100644 index 0000000000000..245ff416643c7 --- /dev/null +++ b/tests/rustdoc-js/alias-sort.js @@ -0,0 +1,10 @@ +// rank doc aliases lower than exact matches +// regression test for + +const EXPECTED = { + 'query': 'foobazbar', + 'others': [ + { 'name': 'foobazbar' }, + { 'name': 'foo' }, + ], +}; diff --git a/tests/rustdoc-js/alias-sort.rs b/tests/rustdoc-js/alias-sort.rs new file mode 100644 index 0000000000000..542bb4be4387e --- /dev/null +++ b/tests/rustdoc-js/alias-sort.rs @@ -0,0 +1,5 @@ +/// asdf +pub fn foobazbar() {} + +#[doc(alias("foobazbar"))] +pub fn foo() {} diff --git a/tests/rustdoc-js/generics-trait.js b/tests/rustdoc-js/generics-trait.js index cd100463e9a85..eb8e99e80f99f 100644 --- a/tests/rustdoc-js/generics-trait.js +++ b/tests/rustdoc-js/generics-trait.js @@ -95,3 +95,24 @@ const EXPECTED = [ ], }, ]; + +const PARSED = [ + { + 'query': 'Result', + 'userQuery': 'Result', + 'foundElems': 1, + 'returned': [], + 'error': null, + 'proposeCorrectionFrom': 'SomeTraiz', + 'proposeCorrectionTo': 'SomeTrait', + }, + { + 'query': 'Result', + 'userQuery': 'Result', + 'foundElems': 1, + 'returned': [], + 'error': null, + 'proposeCorrectionFrom': 'NoSuchTrait', + 'proposeCorrectionTo': null, + }, +]; diff --git a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr index d95876659c7ac..a997fbee1f2a0 100644 --- a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr +++ b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr @@ -30,7 +30,7 @@ LL | == (Error::new(ErrorKind::Other, "2"), thread::current()); | ^^ ------------------------------------------------------ (std::io::Error, Thread) | note: `Thread` does not implement `PartialEq` - --> $SRC_DIR/std/src/thread/mod.rs:LL:COL + --> $SRC_DIR/std/src/thread/thread.rs:LL:COL | = note: `Thread` is defined in another crate note: `std::io::Error` does not implement `PartialEq` @@ -54,7 +54,7 @@ LL | struct T1; LL | struct T2; | ^^^^^^^^^ must implement `PartialEq` note: `Thread` does not implement `PartialEq` - --> $SRC_DIR/std/src/thread/mod.rs:LL:COL + --> $SRC_DIR/std/src/thread/thread.rs:LL:COL | = note: `Thread` is defined in another crate note: `std::io::Error` does not implement `PartialEq` diff --git a/tests/ui/issues/issue-51515.rs b/tests/ui/borrowck/assignment-to-immutable-ref.rs similarity index 59% rename from tests/ui/issues/issue-51515.rs rename to tests/ui/borrowck/assignment-to-immutable-ref.rs index 33a9bf85e23ea..c04c26f6ea2e7 100644 --- a/tests/ui/issues/issue-51515.rs +++ b/tests/ui/borrowck/assignment-to-immutable-ref.rs @@ -1,3 +1,7 @@ +//! Regression test for issue +//! Test that assigning through an immutable reference (`&`) correctly yields +//! an assignment error (E0594) and suggests using a mutable reference. + fn main() { let foo = &16; //~^ HELP consider changing this to be a mutable reference diff --git a/tests/ui/issues/issue-51515.stderr b/tests/ui/borrowck/assignment-to-immutable-ref.stderr similarity index 88% rename from tests/ui/issues/issue-51515.stderr rename to tests/ui/borrowck/assignment-to-immutable-ref.stderr index 5bf33b191c0eb..ec1be1acce0aa 100644 --- a/tests/ui/issues/issue-51515.stderr +++ b/tests/ui/borrowck/assignment-to-immutable-ref.stderr @@ -1,5 +1,5 @@ error[E0594]: cannot assign to `*foo`, which is behind a `&` reference - --> $DIR/issue-51515.rs:4:5 + --> $DIR/assignment-to-immutable-ref.rs:8:5 | LL | *foo = 32; | ^^^^^^^^^ `foo` is a `&` reference, so it cannot be written to @@ -10,7 +10,7 @@ LL | let foo = &mut 16; | +++ error[E0594]: cannot assign to `*bar`, which is behind a `&` reference - --> $DIR/issue-51515.rs:8:5 + --> $DIR/assignment-to-immutable-ref.rs:12:5 | LL | *bar = 64; | ^^^^^^^^^ `bar` is a `&` reference, so it cannot be written to diff --git a/tests/ui/closures/box-generic-closure.rs b/tests/ui/closures/box-generic-closure.rs new file mode 100644 index 0000000000000..e526cf0012d6f --- /dev/null +++ b/tests/ui/closures/box-generic-closure.rs @@ -0,0 +1,10 @@ +//! Regression test for issue +//! Test that anonymous closure types cannot be coerced to a generic type +//! parameter (F: FnMut()) when trying to box them. + +fn foo() { + let _: Box = Box::new(|| ()); + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/issues/issue-51154.stderr b/tests/ui/closures/box-generic-closure.stderr similarity index 84% rename from tests/ui/issues/issue-51154.stderr rename to tests/ui/closures/box-generic-closure.stderr index b7451ea28ee97..9b74a196c3ad4 100644 --- a/tests/ui/issues/issue-51154.stderr +++ b/tests/ui/closures/box-generic-closure.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-51154.rs:2:30 + --> $DIR/box-generic-closure.rs:6:30 | LL | fn foo() { | - expected this type parameter @@ -9,7 +9,7 @@ LL | let _: Box = Box::new(|| ()); | arguments to this function are incorrect | = note: expected type parameter `F` - found closure `{closure@$DIR/issue-51154.rs:2:30: 2:32}` + found closure `{closure@$DIR/box-generic-closure.rs:6:30: 6:32}` = help: every closure has a distinct type and so could not always match the caller-chosen type of parameter `F` note: associated function defined here --> $SRC_DIR/alloc/src/boxed.rs:LL:COL diff --git a/tests/ui/closures/closure-move-sync.stderr b/tests/ui/closures/closure-move-sync.stderr index 2bb26b0c0b7d4..7241f78c3539e 100644 --- a/tests/ui/closures/closure-move-sync.stderr +++ b/tests/ui/closures/closure-move-sync.stderr @@ -18,7 +18,7 @@ note: required because it's used within this closure LL | let t = thread::spawn(|| { | ^^ note: required by a bound in `spawn` - --> $SRC_DIR/std/src/thread/mod.rs:LL:COL + --> $SRC_DIR/std/src/thread/functions.rs:LL:COL error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/type-dependent/issue-71805.rs b/tests/ui/const-generics/type-dependent/issue-71805.rs index 27c101df107c8..de04a809da60f 100644 --- a/tests/ui/const-generics/type-dependent/issue-71805.rs +++ b/tests/ui/const-generics/type-dependent/issue-71805.rs @@ -4,7 +4,7 @@ use std::mem::MaybeUninit; trait CollectSlice<'a>: Iterator { fn inner_array(&mut self) -> [Self::Item; N]; - fn collect_array(&mut self) -> [Self::Item; N] { + fn custom_collect_array(&mut self) -> [Self::Item; N] { let result = self.inner_array(); assert!(self.next().is_none()); result @@ -34,5 +34,5 @@ where fn main() { let mut foos = [0u64; 9].iter().cloned(); - let _bar: [u64; 9] = foos.collect_array::<9_usize>(); + let _bar: [u64; 9] = foos.custom_collect_array::<9_usize>(); } diff --git a/tests/ui/consts/const-eval/const_raw_ptr_ops.gated.stderr b/tests/ui/consts/const-eval/const_raw_ptr_ops.gated.stderr new file mode 100644 index 0000000000000..c39048c8f283c --- /dev/null +++ b/tests/ui/consts/const-eval/const_raw_ptr_ops.gated.stderr @@ -0,0 +1,23 @@ +error[E0277]: pointers cannot be reliably compared during const eval + --> $DIR/const_raw_ptr_ops.rs:7:26 + | +LL | const X: bool = unsafe { &1 as *const i32 == &2 as *const i32 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: trait `PartialEq` is implemented but not `const` + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL + = note: see issue #53020 for more information + +error[E0277]: pointers cannot be reliably compared during const eval + --> $DIR/const_raw_ptr_ops.rs:9:27 + | +LL | const X2: bool = unsafe { 42 as *const i32 == 43 as *const i32 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: trait `PartialEq` is implemented but not `const` + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL + = note: see issue #53020 for more information + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/consts/const-eval/const_raw_ptr_ops.rs b/tests/ui/consts/const-eval/const_raw_ptr_ops.rs index 432a05756d305..c26ef164ebab9 100644 --- a/tests/ui/consts/const-eval/const_raw_ptr_ops.rs +++ b/tests/ui/consts/const-eval/const_raw_ptr_ops.rs @@ -1,3 +1,6 @@ +//@revisions: stable gated +#![cfg_attr(gated, feature(const_trait_impl))] + fn main() {} // unconst and bad, will thus error in miri diff --git a/tests/ui/consts/const-eval/const_raw_ptr_ops.stderr b/tests/ui/consts/const-eval/const_raw_ptr_ops.stable.stderr similarity index 89% rename from tests/ui/consts/const-eval/const_raw_ptr_ops.stderr rename to tests/ui/consts/const-eval/const_raw_ptr_ops.stable.stderr index 1f5bca273d3b0..2c7e6e8671351 100644 --- a/tests/ui/consts/const-eval/const_raw_ptr_ops.stderr +++ b/tests/ui/consts/const-eval/const_raw_ptr_ops.stable.stderr @@ -1,5 +1,5 @@ error: pointers cannot be reliably compared during const eval - --> $DIR/const_raw_ptr_ops.rs:4:26 + --> $DIR/const_raw_ptr_ops.rs:7:26 | LL | const X: bool = unsafe { &1 as *const i32 == &2 as *const i32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | const X: bool = unsafe { &1 as *const i32 == &2 as *const i32 }; = note: see issue #53020 for more information error: pointers cannot be reliably compared during const eval - --> $DIR/const_raw_ptr_ops.rs:6:27 + --> $DIR/const_raw_ptr_ops.rs:9:27 | LL | const X2: bool = unsafe { 42 as *const i32 == 43 as *const i32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/different-fn-ptr-binders-during-ctfe.gated.stderr b/tests/ui/consts/different-fn-ptr-binders-during-ctfe.gated.stderr new file mode 100644 index 0000000000000..f13e0c6661814 --- /dev/null +++ b/tests/ui/consts/different-fn-ptr-binders-during-ctfe.gated.stderr @@ -0,0 +1,13 @@ +error[E0277]: pointers cannot be reliably compared during const eval + --> $DIR/different-fn-ptr-binders-during-ctfe.rs:5:5 + | +LL | x == y + | ^^^^^^ + | +note: trait `PartialEq` is implemented but not `const` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL + = note: see issue #53020 for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/consts/different-fn-ptr-binders-during-ctfe.rs b/tests/ui/consts/different-fn-ptr-binders-during-ctfe.rs index b378542e5730c..7f963eabd6d56 100644 --- a/tests/ui/consts/different-fn-ptr-binders-during-ctfe.rs +++ b/tests/ui/consts/different-fn-ptr-binders-during-ctfe.rs @@ -1,3 +1,6 @@ +//@revisions: stable gated +#![cfg_attr(gated, feature(const_trait_impl))] + const fn cmp(x: fn(&'static ()), y: for<'a> fn(&'a ())) -> bool { x == y //~^ ERROR pointers cannot be reliably compared during const eval diff --git a/tests/ui/consts/different-fn-ptr-binders-during-ctfe.stderr b/tests/ui/consts/different-fn-ptr-binders-during-ctfe.stable.stderr similarity index 81% rename from tests/ui/consts/different-fn-ptr-binders-during-ctfe.stderr rename to tests/ui/consts/different-fn-ptr-binders-during-ctfe.stable.stderr index 43a7b9ce66ce0..92b09e7db0d45 100644 --- a/tests/ui/consts/different-fn-ptr-binders-during-ctfe.stderr +++ b/tests/ui/consts/different-fn-ptr-binders-during-ctfe.stable.stderr @@ -1,5 +1,5 @@ error: pointers cannot be reliably compared during const eval - --> $DIR/different-fn-ptr-binders-during-ctfe.rs:2:5 + --> $DIR/different-fn-ptr-binders-during-ctfe.rs:5:5 | LL | x == y | ^^^^^^ diff --git a/tests/ui/consts/issue-94675.stderr b/tests/ui/consts/issue-94675.stderr index d7664de5c07a7..771f2a14c3053 100644 --- a/tests/ui/consts/issue-94675.stderr +++ b/tests/ui/consts/issue-94675.stderr @@ -3,12 +3,18 @@ error[E0277]: the trait bound `Vec: [const] Index<_>` is not satisfied | LL | self.bar[0] = baz.len(); | ^^^^^^^^^^^ + | +note: trait `Index` is implemented but not `const` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL error[E0277]: the trait bound `Vec: [const] IndexMut` is not satisfied --> $DIR/issue-94675.rs:11:9 | LL | self.bar[0] = baz.len(); | ^^^^^^^^^^^ + | +note: trait `IndexMut` is implemented but not `const` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL error[E0277]: the trait bound `Vec: [const] Index` is not satisfied --> $DIR/issue-94675.rs:11:9 @@ -16,6 +22,8 @@ error[E0277]: the trait bound `Vec: [const] Index` is not satisfie LL | self.bar[0] = baz.len(); | ^^^^^^^^^^^ | +note: trait `Index` is implemented but not `const` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL note: required by a bound in `std::ops::IndexMut::index_mut` --> $SRC_DIR/core/src/ops/index.rs:LL:COL diff --git a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs new file mode 100644 index 0000000000000..c0af549e2d012 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs @@ -0,0 +1,32 @@ +#![feature(const_trait_impl, const_cmp)] +#![deny(misplaced_diagnostic_attributes)] + +#[diagnostic::on_const(message = "tadaa", note = "boing")] +//~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls +pub struct Foo; + +#[diagnostic::on_const(message = "tadaa", note = "boing")] +//~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls +impl const PartialEq for Foo { + fn eq(&self, _other: &Foo) -> bool { + true + } +} + +#[diagnostic::on_const(message = "tadaa", note = "boing")] +//~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls +impl Foo { + fn eq(&self, _other: &Foo) -> bool { + true + } +} + +impl PartialOrd for Foo { + #[diagnostic::on_const(message = "tadaa", note = "boing")] + //~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls + fn partial_cmp(&self, other: &Foo) -> Option { + None + } +} + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr new file mode 100644 index 0000000000000..a12c00c74d055 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr @@ -0,0 +1,32 @@ +error: `#[diagnostic::on_const]` can only be applied to trait impls + --> $DIR/misplaced_attr.rs:4:1 + | +LL | #[diagnostic::on_const(message = "tadaa", note = "boing")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/misplaced_attr.rs:2:9 + | +LL | #![deny(misplaced_diagnostic_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[diagnostic::on_const]` can only be applied to trait impls + --> $DIR/misplaced_attr.rs:8:1 + | +LL | #[diagnostic::on_const(message = "tadaa", note = "boing")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[diagnostic::on_const]` can only be applied to trait impls + --> $DIR/misplaced_attr.rs:16:1 + | +LL | #[diagnostic::on_const(message = "tadaa", note = "boing")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[diagnostic::on_const]` can only be applied to trait impls + --> $DIR/misplaced_attr.rs:25:5 + | +LL | #[diagnostic::on_const(message = "tadaa", note = "boing")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/errors/remap-path-prefix-sysroot.with-remap.stderr b/tests/ui/errors/remap-path-prefix-sysroot.with-remap.stderr index 88d713d2b048c..8540f492a662a 100644 --- a/tests/ui/errors/remap-path-prefix-sysroot.with-remap.stderr +++ b/tests/ui/errors/remap-path-prefix-sysroot.with-remap.stderr @@ -7,7 +7,7 @@ LL | self.thread.join().unwrap(); | move occurs because `self.thread` has type `JoinHandle<()>`, which does not implement the `Copy` trait | note: `JoinHandle::::join` takes ownership of the receiver `self`, which moves `self.thread` - --> remapped/library/std/src/thread/mod.rs:LL:COL + --> remapped/library/std/src/thread/join_handle.rs:LL:COL | LL | pub fn join(self) -> Result { | ^^^^ diff --git a/tests/ui/errors/remap-path-prefix-sysroot.without-remap.stderr b/tests/ui/errors/remap-path-prefix-sysroot.without-remap.stderr index 9b337699c3295..91a3d5b5d6c8a 100644 --- a/tests/ui/errors/remap-path-prefix-sysroot.without-remap.stderr +++ b/tests/ui/errors/remap-path-prefix-sysroot.without-remap.stderr @@ -7,7 +7,7 @@ LL | self.thread.join().unwrap(); | move occurs because `self.thread` has type `JoinHandle<()>`, which does not implement the `Copy` trait | note: `JoinHandle::::join` takes ownership of the receiver `self`, which moves `self.thread` - --> $SRC_DIR_REAL/std/src/thread/mod.rs:LL:COL + --> $SRC_DIR_REAL/std/src/thread/join_handle.rs:LL:COL | LL | pub fn join(self) -> Result { | ^^^^ diff --git a/tests/ui/feature-gates/auxiliary/diagnostic-on-const.rs b/tests/ui/feature-gates/auxiliary/diagnostic-on-const.rs new file mode 100644 index 0000000000000..3d2b5b9a9239f --- /dev/null +++ b/tests/ui/feature-gates/auxiliary/diagnostic-on-const.rs @@ -0,0 +1,8 @@ +pub struct Foo; + +#[diagnostic::on_const(message = "tadaa", note = "boing")] +impl PartialEq for Foo { + fn eq(&self, _other: &Foo) -> bool { + true + } +} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-const.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-const.rs new file mode 100644 index 0000000000000..398fa30e74047 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-const.rs @@ -0,0 +1,16 @@ +//! This is an unusual feature gate test, as it doesn't test the feature +//! gate, but the fact that not adding the feature gate to +//! the aux crate will cause the diagnostic to not emit the +//! custom diagnostic message + +//@ aux-build: diagnostic-on-const.rs + +extern crate diagnostic_on_const; +use diagnostic_on_const::Foo; + +const fn foo() { + Foo == Foo; + //~^ ERROR: cannot call non-const operator in constant functions +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-const.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-const.stderr new file mode 100644 index 0000000000000..04c901f4f938b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-const.stderr @@ -0,0 +1,16 @@ +error[E0015]: cannot call non-const operator in constant functions + --> $DIR/feature-gate-diagnostic-on-const.rs:12:5 + | +LL | Foo == Foo; + | ^^^^^^^^^^ + | +note: impl defined here, but it is not `const` + --> $DIR/auxiliary/diagnostic-on-const.rs:4:1 + | +LL | impl PartialEq for Foo { + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/issues/issue-51154.rs b/tests/ui/issues/issue-51154.rs deleted file mode 100644 index 12903f79010cb..0000000000000 --- a/tests/ui/issues/issue-51154.rs +++ /dev/null @@ -1,6 +0,0 @@ -fn foo() { - let _: Box = Box::new(|| ()); - //~^ ERROR mismatched types -} - -fn main() {} diff --git a/tests/ui/issues/issue-52717.rs b/tests/ui/issues/issue-52717.rs deleted file mode 100644 index f83232a4a2689..0000000000000 --- a/tests/ui/issues/issue-52717.rs +++ /dev/null @@ -1,13 +0,0 @@ -enum A { - A { - foo: usize, - } -} - -fn main() { - let x = A::A { foo: 3 }; - match x { - A::A { fob } => { println!("{}", fob); } -//~^ ERROR does not have a field named `fob` - } -} diff --git a/tests/ui/issues/issue-52717.stderr b/tests/ui/issues/issue-52717.stderr deleted file mode 100644 index ab8c37225cae6..0000000000000 --- a/tests/ui/issues/issue-52717.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0026]: variant `A::A` does not have a field named `fob` - --> $DIR/issue-52717.rs:10:12 - | -LL | A::A { fob } => { println!("{}", fob); } - | ^^^ - | | - | variant `A::A` does not have this field - | help: a field with a similar name exists: `foo` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0026`. diff --git a/tests/ui/mir/mir-inlining/ice-issue-77564.rs b/tests/ui/mir/mir-inlining/ice-issue-77564.rs index fce6d1d174f66..256ff295184d7 100644 --- a/tests/ui/mir/mir-inlining/ice-issue-77564.rs +++ b/tests/ui/mir/mir-inlining/ice-issue-77564.rs @@ -29,10 +29,11 @@ where fn main() { assert_eq!( - [[1, 2], [3, 4]] + CollectArray::collect_array( + &mut [[1, 2], [3, 4]] .iter() - .map(|row| row.iter().collect_array()) - .collect_array(), + .map(|row| CollectArray::collect_array(&mut row.iter())) + ), [[&1, &2], [&3, &4]] ); } diff --git a/tests/ui/issues/issue-53348.rs b/tests/ui/mismatched_types/assign-deref-string-to-string.rs similarity index 52% rename from tests/ui/issues/issue-53348.rs rename to tests/ui/mismatched_types/assign-deref-string-to-string.rs index 66800d9e92961..82498a6e63e21 100644 --- a/tests/ui/issues/issue-53348.rs +++ b/tests/ui/mismatched_types/assign-deref-string-to-string.rs @@ -1,9 +1,13 @@ +//! Regression test for issue https://github.com/rust-lang/rust/issues/53348 +//! Test that attempting to assign a dereferenced `String` (which is `str`) +//! to a `String` variable correctly results in a mismatched types error (E0308). + fn main() { let mut v = vec!["hello", "this", "is", "a", "test"]; let v2 = Vec::new(); - v.into_iter().map(|s|s.to_owned()).collect::>(); + v.into_iter().map(|s| s.to_owned()).collect::>(); let mut a = String::new(); //~ NOTE expected due to this value for i in v { diff --git a/tests/ui/issues/issue-53348.stderr b/tests/ui/mismatched_types/assign-deref-string-to-string.stderr similarity index 87% rename from tests/ui/issues/issue-53348.stderr rename to tests/ui/mismatched_types/assign-deref-string-to-string.stderr index 38fa98e65e138..8cc3cb6b9bef0 100644 --- a/tests/ui/issues/issue-53348.stderr +++ b/tests/ui/mismatched_types/assign-deref-string-to-string.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-53348.rs:10:13 + --> $DIR/assign-deref-string-to-string.rs:14:13 | LL | let mut a = String::new(); | ------------- expected due to this value diff --git a/tests/ui/pattern/match-enum-struct-variant-field-missing.rs b/tests/ui/pattern/match-enum-struct-variant-field-missing.rs new file mode 100644 index 0000000000000..0a281b0efe052 --- /dev/null +++ b/tests/ui/pattern/match-enum-struct-variant-field-missing.rs @@ -0,0 +1,17 @@ +//! Regression test for issue +//! Test that matching an enum struct variant with a missing or incorrect field name +//! correctly yields a "does not have a field named" error. + +enum A { + A { foo: usize }, +} + +fn main() { + let x = A::A { foo: 3 }; + match x { + A::A { fob } => { + //~^ ERROR does not have a field named `fob` + println!("{fob}"); + } + } +} diff --git a/tests/ui/pattern/match-enum-struct-variant-field-missing.stderr b/tests/ui/pattern/match-enum-struct-variant-field-missing.stderr new file mode 100644 index 0000000000000..326595a7cb706 --- /dev/null +++ b/tests/ui/pattern/match-enum-struct-variant-field-missing.stderr @@ -0,0 +1,12 @@ +error[E0026]: variant `A::A` does not have a field named `fob` + --> $DIR/match-enum-struct-variant-field-missing.rs:12:16 + | +LL | A::A { fob } => { + | ^^^ + | | + | variant `A::A` does not have this field + | help: a field with a similar name exists: `foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0026`. diff --git a/tests/ui/threads-sendsync/rc-is-not-send.stderr b/tests/ui/threads-sendsync/rc-is-not-send.stderr index a06b683f729e0..784a0999c6011 100644 --- a/tests/ui/threads-sendsync/rc-is-not-send.stderr +++ b/tests/ui/threads-sendsync/rc-is-not-send.stderr @@ -30,7 +30,7 @@ note: required because it's used within this closure LL | thread::spawn(move || { | ^^^^^^^ note: required by a bound in `spawn` - --> $SRC_DIR/std/src/thread/mod.rs:LL:COL + --> $SRC_DIR/std/src/thread/functions.rs:LL:COL error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/call-generic-method-fail.stderr b/tests/ui/traits/const-traits/call-generic-method-fail.stderr index 6e75730b9b513..a2fba141f7b89 100644 --- a/tests/ui/traits/const-traits/call-generic-method-fail.stderr +++ b/tests/ui/traits/const-traits/call-generic-method-fail.stderr @@ -3,11 +3,6 @@ error[E0277]: the trait bound `T: [const] PartialEq` is not satisfied | LL | *t == *t | ^^^^^^^^ - | -note: trait `PartialEq` is implemented but not `const` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL -note: trait `PartialEq` is implemented but not `const` - --> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-53300.rs b/tests/ui/type/cannot-find-wrapper-with-impl-trait.rs similarity index 53% rename from tests/ui/issues/issue-53300.rs rename to tests/ui/type/cannot-find-wrapper-with-impl-trait.rs index 09f0fe9d93553..48ff4c38f7db3 100644 --- a/tests/ui/issues/issue-53300.rs +++ b/tests/ui/type/cannot-find-wrapper-with-impl-trait.rs @@ -1,4 +1,5 @@ -// issue 53300 +//! Regression test for issue +//! Tests that an undefined type (Wrapper) used with impl Trait correctly gives E0412. pub trait A { fn add(&self, b: i32) -> i32; diff --git a/tests/ui/issues/issue-53300.stderr b/tests/ui/type/cannot-find-wrapper-with-impl-trait.stderr similarity index 82% rename from tests/ui/issues/issue-53300.stderr rename to tests/ui/type/cannot-find-wrapper-with-impl-trait.stderr index 293465ecb8140..ac7f2e5134a2c 100644 --- a/tests/ui/issues/issue-53300.stderr +++ b/tests/ui/type/cannot-find-wrapper-with-impl-trait.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `Wrapper` in this scope - --> $DIR/issue-53300.rs:7:18 + --> $DIR/cannot-find-wrapper-with-impl-trait.rs:8:18 | LL | fn addition() -> Wrapper {} | ^^^^^^^ not found in this scope