Auto merge of #106324 - compiler-errors:rollup-2m9njin, r=compiler-errors
Rollup of 9 pull requests Successful merges: - #105903 (Unify id-based thread parking implementations) - #106232 (CFI: Monomorphize transparent ADTs before typeid) - #106248 (Revert "Implement allow-by-default `multiple_supertrait_upcastable` lint") - #106286 (Make tidy errors red) - #106295 (Extend scraped examples layout GUI test for position of buttons) - #106305 ( bootstrap: Get rid of tail_args in stream_cargo) - #106310 (Dont use `--merge-base` during bootstrap formatting subcommand) - #106314 (Fix panic on `x build --help`) - #106317 (Only deduplicate stack traces for good path bugs) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
@@ -2675,9 +2675,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owo-colors"
|
name = "owo-colors"
|
||||||
version = "3.4.0"
|
version = "3.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b"
|
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packed_simd_2"
|
name = "packed_simd_2"
|
||||||
@@ -5203,9 +5203,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.1.2"
|
version = "1.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
@@ -5309,6 +5309,7 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
"miropt-test-tools",
|
"miropt-test-tools",
|
||||||
"regex",
|
"regex",
|
||||||
|
"termcolor",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1199,8 +1199,8 @@ static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send +
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Invoke the default handler, which prints the actual panic message and optionally a backtrace
|
// Invoke the default handler, which prints the actual panic message and optionally a backtrace
|
||||||
// Don't do this for `ExplicitBug`, which has an unhelpful message and backtrace.
|
// Don't do this for `GoodPathBug`, which already emits its own more useful backtrace.
|
||||||
if !info.payload().is::<rustc_errors::ExplicitBug>() {
|
if !info.payload().is::<rustc_errors::GoodPathBug>() {
|
||||||
(*DEFAULT_HOOK)(info);
|
(*DEFAULT_HOOK)(info);
|
||||||
|
|
||||||
// Separate the output with an empty line
|
// Separate the output with an empty line
|
||||||
@@ -1237,7 +1237,9 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
|
|||||||
|
|
||||||
// a .span_bug or .bug call has already printed what
|
// a .span_bug or .bug call has already printed what
|
||||||
// it wants to print.
|
// it wants to print.
|
||||||
if !info.payload().is::<rustc_errors::ExplicitBug>() {
|
if !info.payload().is::<rustc_errors::ExplicitBug>()
|
||||||
|
&& !info.payload().is::<rustc_errors::GoodPathBug>()
|
||||||
|
{
|
||||||
let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
|
let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
|
||||||
handler.emit_diagnostic(&mut d);
|
handler.emit_diagnostic(&mut d);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,12 +40,13 @@ use rustc_span::source_map::SourceMap;
|
|||||||
use rustc_span::HashStableContext;
|
use rustc_span::HashStableContext;
|
||||||
use rustc_span::{Loc, Span};
|
use rustc_span::{Loc, Span};
|
||||||
|
|
||||||
|
use std::any::Any;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::fmt;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use std::panic;
|
use std::panic;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{error, fmt};
|
|
||||||
|
|
||||||
use termcolor::{Color, ColorSpec};
|
use termcolor::{Color, ColorSpec};
|
||||||
|
|
||||||
@@ -361,16 +362,11 @@ pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
|
|||||||
|
|
||||||
/// Signifies that the compiler died with an explicit call to `.bug`
|
/// Signifies that the compiler died with an explicit call to `.bug`
|
||||||
/// or `.span_bug` rather than a failed assertion, etc.
|
/// or `.span_bug` rather than a failed assertion, etc.
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub struct ExplicitBug;
|
pub struct ExplicitBug;
|
||||||
|
|
||||||
impl fmt::Display for ExplicitBug {
|
/// Signifies that the compiler died with an explicit call to `.delay_good_path_bug`
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
/// rather than a failed assertion, etc.
|
||||||
write!(f, "parser internal bug")
|
pub struct GoodPathBug;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::Error for ExplicitBug {}
|
|
||||||
|
|
||||||
pub use diagnostic::{
|
pub use diagnostic::{
|
||||||
AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId,
|
AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId,
|
||||||
@@ -507,7 +503,11 @@ impl Drop for HandlerInner {
|
|||||||
|
|
||||||
if !self.has_errors() {
|
if !self.has_errors() {
|
||||||
let bugs = std::mem::replace(&mut self.delayed_span_bugs, Vec::new());
|
let bugs = std::mem::replace(&mut self.delayed_span_bugs, Vec::new());
|
||||||
self.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued");
|
self.flush_delayed(
|
||||||
|
bugs,
|
||||||
|
"no errors encountered even though `delay_span_bug` issued",
|
||||||
|
ExplicitBug,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(eddyb) this explains what `delayed_good_path_bugs` are!
|
// FIXME(eddyb) this explains what `delayed_good_path_bugs` are!
|
||||||
@@ -520,6 +520,7 @@ impl Drop for HandlerInner {
|
|||||||
self.flush_delayed(
|
self.flush_delayed(
|
||||||
bugs.into_iter().map(DelayedDiagnostic::decorate),
|
bugs.into_iter().map(DelayedDiagnostic::decorate),
|
||||||
"no warnings or errors encountered even though `delayed_good_path_bugs` issued",
|
"no warnings or errors encountered even though `delayed_good_path_bugs` issued",
|
||||||
|
GoodPathBug,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1203,7 +1204,11 @@ impl Handler {
|
|||||||
pub fn flush_delayed(&self) {
|
pub fn flush_delayed(&self) {
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
let bugs = std::mem::replace(&mut inner.delayed_span_bugs, Vec::new());
|
let bugs = std::mem::replace(&mut inner.delayed_span_bugs, Vec::new());
|
||||||
inner.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued");
|
inner.flush_delayed(
|
||||||
|
bugs,
|
||||||
|
"no errors encountered even though `delay_span_bug` issued",
|
||||||
|
ExplicitBug,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1580,6 +1585,7 @@ impl HandlerInner {
|
|||||||
&mut self,
|
&mut self,
|
||||||
bugs: impl IntoIterator<Item = Diagnostic>,
|
bugs: impl IntoIterator<Item = Diagnostic>,
|
||||||
explanation: impl Into<DiagnosticMessage> + Copy,
|
explanation: impl Into<DiagnosticMessage> + Copy,
|
||||||
|
panic_with: impl Any + Send + 'static,
|
||||||
) {
|
) {
|
||||||
let mut no_bugs = true;
|
let mut no_bugs = true;
|
||||||
for mut bug in bugs {
|
for mut bug in bugs {
|
||||||
@@ -1607,7 +1613,7 @@ impl HandlerInner {
|
|||||||
|
|
||||||
// Panic with `ExplicitBug` to avoid "unexpected panic" messages.
|
// Panic with `ExplicitBug` to avoid "unexpected panic" messages.
|
||||||
if !no_bugs {
|
if !no_bugs {
|
||||||
panic::panic_any(ExplicitBug);
|
panic::panic_any(panic_with);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -160,8 +160,6 @@ declare_features! (
|
|||||||
(active, intrinsics, "1.0.0", None, None),
|
(active, intrinsics, "1.0.0", None, None),
|
||||||
/// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic.
|
/// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic.
|
||||||
(active, lang_items, "1.0.0", None, None),
|
(active, lang_items, "1.0.0", None, None),
|
||||||
/// Allows the `multiple_supertrait_upcastable` lint.
|
|
||||||
(active, multiple_supertrait_upcastable, "CURRENT_RUSTC_VERSION", None, None),
|
|
||||||
/// Allows using `#[omit_gdb_pretty_printer_section]`.
|
/// Allows using `#[omit_gdb_pretty_printer_section]`.
|
||||||
(active, omit_gdb_pretty_printer_section, "1.5.0", None, None),
|
(active, omit_gdb_pretty_printer_section, "1.5.0", None, None),
|
||||||
/// Allows using `#[prelude_import]` on glob `use` items.
|
/// Allows using `#[prelude_import]` on glob `use` items.
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ mod late;
|
|||||||
mod let_underscore;
|
mod let_underscore;
|
||||||
mod levels;
|
mod levels;
|
||||||
mod methods;
|
mod methods;
|
||||||
mod multiple_supertrait_upcastable;
|
|
||||||
mod non_ascii_idents;
|
mod non_ascii_idents;
|
||||||
mod non_fmt_panic;
|
mod non_fmt_panic;
|
||||||
mod nonstandard_style;
|
mod nonstandard_style;
|
||||||
@@ -96,7 +95,6 @@ use hidden_unicode_codepoints::*;
|
|||||||
use internal::*;
|
use internal::*;
|
||||||
use let_underscore::*;
|
use let_underscore::*;
|
||||||
use methods::*;
|
use methods::*;
|
||||||
use multiple_supertrait_upcastable::*;
|
|
||||||
use non_ascii_idents::*;
|
use non_ascii_idents::*;
|
||||||
use non_fmt_panic::NonPanicFmt;
|
use non_fmt_panic::NonPanicFmt;
|
||||||
use nonstandard_style::*;
|
use nonstandard_style::*;
|
||||||
@@ -231,7 +229,6 @@ late_lint_methods!(
|
|||||||
InvalidAtomicOrdering: InvalidAtomicOrdering,
|
InvalidAtomicOrdering: InvalidAtomicOrdering,
|
||||||
NamedAsmLabels: NamedAsmLabels,
|
NamedAsmLabels: NamedAsmLabels,
|
||||||
OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
|
OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
|
||||||
MultipleSupertraitUpcastable: MultipleSupertraitUpcastable,
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
use crate::{LateContext, LateLintPass, LintContext};
|
|
||||||
|
|
||||||
use rustc_errors::DelayDm;
|
|
||||||
use rustc_hir as hir;
|
|
||||||
use rustc_span::sym;
|
|
||||||
|
|
||||||
declare_lint! {
|
|
||||||
/// The `multiple_supertrait_upcastable` lint detects when an object-safe trait has multiple
|
|
||||||
/// supertraits.
|
|
||||||
///
|
|
||||||
/// ### Example
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// trait A {}
|
|
||||||
/// trait B {}
|
|
||||||
///
|
|
||||||
/// #[warn(multiple_supertrait_upcastable)]
|
|
||||||
/// trait C: A + B {}
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// {{produces}}
|
|
||||||
///
|
|
||||||
/// ### Explanation
|
|
||||||
///
|
|
||||||
/// To support upcasting with multiple supertraits, we need to store multiple vtables and this
|
|
||||||
/// can result in extra space overhead, even if no code actually uses upcasting.
|
|
||||||
/// This lint allows users to identify when such scenarios occur and to decide whether the
|
|
||||||
/// additional overhead is justified.
|
|
||||||
pub MULTIPLE_SUPERTRAIT_UPCASTABLE,
|
|
||||||
Allow,
|
|
||||||
"detect when an object-safe trait has multiple supertraits",
|
|
||||||
@feature_gate = sym::multiple_supertrait_upcastable;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare_lint_pass!(MultipleSupertraitUpcastable => [MULTIPLE_SUPERTRAIT_UPCASTABLE]);
|
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable {
|
|
||||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
|
|
||||||
let def_id = item.owner_id.to_def_id();
|
|
||||||
if let hir::ItemKind::Trait(_, _, _, _, _) = item.kind
|
|
||||||
&& cx.tcx.is_object_safe(def_id)
|
|
||||||
{
|
|
||||||
let direct_super_traits_iter = cx.tcx
|
|
||||||
.super_predicates_of(def_id)
|
|
||||||
.predicates
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|(pred, _)| pred.to_opt_poly_trait_pred());
|
|
||||||
if direct_super_traits_iter.count() > 1 {
|
|
||||||
cx.struct_span_lint(
|
|
||||||
MULTIPLE_SUPERTRAIT_UPCASTABLE,
|
|
||||||
cx.tcx.def_span(def_id),
|
|
||||||
DelayDm(|| {
|
|
||||||
format!(
|
|
||||||
"`{}` is object-safe and has multiple supertraits",
|
|
||||||
item.ident,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
|diag| diag,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -944,7 +944,6 @@ symbols! {
|
|||||||
mul,
|
mul,
|
||||||
mul_assign,
|
mul_assign,
|
||||||
mul_with_overflow,
|
mul_with_overflow,
|
||||||
multiple_supertrait_upcastable,
|
|
||||||
must_not_suspend,
|
must_not_suspend,
|
||||||
must_use,
|
must_use,
|
||||||
naked,
|
naked,
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ fn encode_const<'tcx>(
|
|||||||
|
|
||||||
/// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for
|
/// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for
|
||||||
/// Rust types that are not used at the FFI boundary.
|
/// Rust types that are not used at the FFI boundary.
|
||||||
|
#[instrument(level = "trace", skip(tcx, dict))]
|
||||||
fn encode_fnsig<'tcx>(
|
fn encode_fnsig<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
fn_sig: &FnSig<'tcx>,
|
fn_sig: &FnSig<'tcx>,
|
||||||
@@ -653,6 +654,7 @@ fn encode_ty<'tcx>(
|
|||||||
// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all
|
// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all
|
||||||
// c_void types into unit types unconditionally, and generalizes all pointers if
|
// c_void types into unit types unconditionally, and generalizes all pointers if
|
||||||
// TransformTyOptions::GENERALIZE_POINTERS option is set.
|
// TransformTyOptions::GENERALIZE_POINTERS option is set.
|
||||||
|
#[instrument(level = "trace", skip(tcx))]
|
||||||
fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> {
|
fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> {
|
||||||
let mut ty = ty;
|
let mut ty = ty;
|
||||||
|
|
||||||
@@ -698,7 +700,7 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
|
|||||||
!is_zst
|
!is_zst
|
||||||
});
|
});
|
||||||
if let Some(field) = field {
|
if let Some(field) = field {
|
||||||
let ty0 = tcx.type_of(field.did);
|
let ty0 = tcx.bound_type_of(field.did).subst(tcx, substs);
|
||||||
// Generalize any repr(transparent) user-defined type that is either a pointer
|
// Generalize any repr(transparent) user-defined type that is either a pointer
|
||||||
// or reference, and either references itself or any other type that contains or
|
// or reference, and either references itself or any other type that contains or
|
||||||
// references itself, to avoid a reference cycle.
|
// references itself, to avoid a reference cycle.
|
||||||
@@ -827,6 +829,7 @@ fn transform_substs<'tcx>(
|
|||||||
|
|
||||||
/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor
|
/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor
|
||||||
/// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
|
/// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
|
||||||
|
#[instrument(level = "trace", skip(tcx))]
|
||||||
pub fn typeid_for_fnabi<'tcx>(
|
pub fn typeid_for_fnabi<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||||
|
|||||||
@@ -87,7 +87,6 @@
|
|||||||
#![warn(missing_debug_implementations)]
|
#![warn(missing_debug_implementations)]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![allow(explicit_outlives_requirements)]
|
#![allow(explicit_outlives_requirements)]
|
||||||
#![cfg_attr(not(bootstrap), warn(multiple_supertrait_upcastable))]
|
|
||||||
//
|
//
|
||||||
// Library features:
|
// Library features:
|
||||||
#![feature(alloc_layout_extra)]
|
#![feature(alloc_layout_extra)]
|
||||||
@@ -192,7 +191,6 @@
|
|||||||
#![feature(unsized_fn_params)]
|
#![feature(unsized_fn_params)]
|
||||||
#![feature(c_unwind)]
|
#![feature(c_unwind)]
|
||||||
#![feature(with_negative_coherence)]
|
#![feature(with_negative_coherence)]
|
||||||
#![cfg_attr(not(bootstrap), feature(multiple_supertrait_upcastable))]
|
|
||||||
//
|
//
|
||||||
// Rustdoc features:
|
// Rustdoc features:
|
||||||
#![feature(doc_cfg)]
|
#![feature(doc_cfg)]
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ use crate::fmt::{Debug, Display};
|
|||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[cfg_attr(not(test), rustc_diagnostic_item = "Error")]
|
#[cfg_attr(not(test), rustc_diagnostic_item = "Error")]
|
||||||
#[rustc_has_incoherent_inherent_impls]
|
#[rustc_has_incoherent_inherent_impls]
|
||||||
#[cfg_attr(not(bootstrap), allow(multiple_supertrait_upcastable))]
|
|
||||||
pub trait Error: Debug + Display {
|
pub trait Error: Debug + Display {
|
||||||
/// The lower-level source of this error, if any.
|
/// The lower-level source of this error, if any.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -95,7 +95,6 @@
|
|||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![allow(explicit_outlives_requirements)]
|
#![allow(explicit_outlives_requirements)]
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
#![cfg_attr(not(bootstrap), warn(multiple_supertrait_upcastable))]
|
|
||||||
//
|
//
|
||||||
// Library features:
|
// Library features:
|
||||||
#![feature(const_align_offset)]
|
#![feature(const_align_offset)]
|
||||||
@@ -232,7 +231,6 @@
|
|||||||
#![feature(unsized_fn_params)]
|
#![feature(unsized_fn_params)]
|
||||||
#![feature(asm_const)]
|
#![feature(asm_const)]
|
||||||
#![feature(const_transmute_copy)]
|
#![feature(const_transmute_copy)]
|
||||||
#![cfg_attr(not(bootstrap), feature(multiple_supertrait_upcastable))]
|
|
||||||
//
|
//
|
||||||
// Target features:
|
// Target features:
|
||||||
#![feature(arm_target_feature)]
|
#![feature(arm_target_feature)]
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ pub mod process;
|
|||||||
pub mod stdio;
|
pub mod stdio;
|
||||||
pub mod thread;
|
pub mod thread;
|
||||||
pub mod thread_local_key;
|
pub mod thread_local_key;
|
||||||
pub mod thread_parker;
|
pub mod thread_parking;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
mod condvar;
|
mod condvar;
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ mod task_queue {
|
|||||||
/// execution. The signal is sent once all TLS destructors have finished at
|
/// execution. The signal is sent once all TLS destructors have finished at
|
||||||
/// which point no new thread locals should be created.
|
/// which point no new thread locals should be created.
|
||||||
pub mod wait_notify {
|
pub mod wait_notify {
|
||||||
use super::super::thread_parker::Parker;
|
|
||||||
use crate::pin::Pin;
|
use crate::pin::Pin;
|
||||||
use crate::sync::Arc;
|
use crate::sync::Arc;
|
||||||
|
use crate::sys_common::thread_parking::Parker;
|
||||||
|
|
||||||
pub struct Notifier(Arc<Parker>);
|
pub struct Notifier(Arc<Parker>);
|
||||||
|
|
||||||
@@ -87,14 +87,14 @@ pub mod wait_notify {
|
|||||||
/// called, this will return immediately, otherwise the current thread
|
/// called, this will return immediately, otherwise the current thread
|
||||||
/// is blocked until notified.
|
/// is blocked until notified.
|
||||||
pub fn wait(self) {
|
pub fn wait(self) {
|
||||||
// This is not actually `unsafe`, but it uses the `Parker` API,
|
// SAFETY:
|
||||||
// which needs `unsafe` on some platforms.
|
// This is only ever called on one thread.
|
||||||
unsafe { Pin::new(&*self.0).park() }
|
unsafe { Pin::new(&*self.0).park() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() -> (Notifier, Waiter) {
|
pub fn new() -> (Notifier, Waiter) {
|
||||||
let inner = Arc::new(Parker::new_internal());
|
let inner = Arc::new(Parker::new());
|
||||||
(Notifier(inner.clone()), Waiter(inner))
|
(Notifier(inner.clone()), Waiter(inner))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,107 +0,0 @@
|
|||||||
//! Thread parking based on SGX events.
|
|
||||||
|
|
||||||
use super::abi::{thread, usercalls};
|
|
||||||
use crate::io::ErrorKind;
|
|
||||||
use crate::pin::Pin;
|
|
||||||
use crate::ptr::{self, NonNull};
|
|
||||||
use crate::sync::atomic::AtomicPtr;
|
|
||||||
use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
|
|
||||||
use crate::time::Duration;
|
|
||||||
use fortanix_sgx_abi::{EV_UNPARK, WAIT_INDEFINITE};
|
|
||||||
|
|
||||||
// The TCS structure must be page-aligned (this is checked by EENTER), so these cannot
|
|
||||||
// be valid pointers
|
|
||||||
const EMPTY: *mut u8 = ptr::invalid_mut(1);
|
|
||||||
const NOTIFIED: *mut u8 = ptr::invalid_mut(2);
|
|
||||||
|
|
||||||
pub struct Parker {
|
|
||||||
/// The park state. One of EMPTY, NOTIFIED or a TCS address.
|
|
||||||
/// A state change to NOTIFIED must be done with release ordering
|
|
||||||
/// and be observed with acquire ordering so that operations after
|
|
||||||
/// `thread::park` returns will not occur before the unpark message
|
|
||||||
/// was sent.
|
|
||||||
state: AtomicPtr<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parker {
|
|
||||||
/// Construct the thread parker. The UNIX parker implementation
|
|
||||||
/// requires this to happen in-place.
|
|
||||||
pub unsafe fn new(parker: *mut Parker) {
|
|
||||||
unsafe { parker.write(Parker::new_internal()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn new_internal() -> Parker {
|
|
||||||
Parker { state: AtomicPtr::new(EMPTY) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
|
|
||||||
pub unsafe fn park(self: Pin<&Self>) {
|
|
||||||
if self.state.load(Acquire) != NOTIFIED {
|
|
||||||
let mut prev = EMPTY;
|
|
||||||
loop {
|
|
||||||
// Guard against changing TCS addresses by always setting the state to
|
|
||||||
// the current value.
|
|
||||||
let tcs = thread::current().as_ptr();
|
|
||||||
if self.state.compare_exchange(prev, tcs, Relaxed, Acquire).is_ok() {
|
|
||||||
let event = usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap();
|
|
||||||
assert!(event & EV_UNPARK == EV_UNPARK);
|
|
||||||
prev = tcs;
|
|
||||||
} else {
|
|
||||||
// The state was definitely changed by another thread at this point.
|
|
||||||
// The only time this occurs is when the state is changed to NOTIFIED.
|
|
||||||
// We observed this change with acquire ordering, so we can simply
|
|
||||||
// change the state to EMPTY with a relaxed store.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, the token was definately read with acquire ordering,
|
|
||||||
// so this can be a relaxed store.
|
|
||||||
self.state.store(EMPTY, Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
|
|
||||||
pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
|
|
||||||
let timeout = u128::min(dur.as_nanos(), WAIT_INDEFINITE as u128 - 1) as u64;
|
|
||||||
let tcs = thread::current().as_ptr();
|
|
||||||
|
|
||||||
if self.state.load(Acquire) != NOTIFIED {
|
|
||||||
if self.state.compare_exchange(EMPTY, tcs, Relaxed, Acquire).is_ok() {
|
|
||||||
match usercalls::wait(EV_UNPARK, timeout) {
|
|
||||||
Ok(event) => assert!(event & EV_UNPARK == EV_UNPARK),
|
|
||||||
Err(e) => {
|
|
||||||
assert!(matches!(e.kind(), ErrorKind::TimedOut | ErrorKind::WouldBlock))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap to provide acquire ordering even if the timeout occurred
|
|
||||||
// before the token was set. This situation can result in spurious
|
|
||||||
// wakeups on the next call to `park_timeout`, but it is better to let
|
|
||||||
// those be handled by the user than do some perhaps unnecessary, but
|
|
||||||
// always expensive guarding.
|
|
||||||
self.state.swap(EMPTY, Acquire);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The token was already read with `acquire` ordering, this can be a store.
|
|
||||||
self.state.store(EMPTY, Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This implementation doesn't require `Pin`, but other implementations do.
|
|
||||||
pub fn unpark(self: Pin<&Self>) {
|
|
||||||
let state = self.state.swap(NOTIFIED, Release);
|
|
||||||
|
|
||||||
if !matches!(state, EMPTY | NOTIFIED) {
|
|
||||||
// There is a thread waiting, wake it up.
|
|
||||||
let tcs = NonNull::new(state).unwrap();
|
|
||||||
// This will fail if the thread has already terminated or its TCS is destroyed
|
|
||||||
// by the time the signal is sent, but that is fine. If another thread receives
|
|
||||||
// the same TCS, it will receive this notification as a spurious wakeup, but
|
|
||||||
// all users of `wait` should and (internally) do guard against those where
|
|
||||||
// necessary.
|
|
||||||
let _ = usercalls::send(EV_UNPARK, Some(tcs));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
23
library/std/src/sys/sgx/thread_parking.rs
Normal file
23
library/std/src/sys/sgx/thread_parking.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use super::abi::usercalls;
|
||||||
|
use crate::io::ErrorKind;
|
||||||
|
use crate::time::Duration;
|
||||||
|
use fortanix_sgx_abi::{EV_UNPARK, WAIT_INDEFINITE};
|
||||||
|
|
||||||
|
pub type ThreadId = fortanix_sgx_abi::Tcs;
|
||||||
|
|
||||||
|
pub use super::abi::thread::current;
|
||||||
|
|
||||||
|
pub fn park(_hint: usize) {
|
||||||
|
usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn park_timeout(dur: Duration, _hint: usize) {
|
||||||
|
let timeout = u128::min(dur.as_nanos(), WAIT_INDEFINITE as u128 - 1) as u64;
|
||||||
|
if let Err(e) = usercalls::wait(EV_UNPARK, timeout) {
|
||||||
|
assert!(matches!(e.kind(), ErrorKind::TimedOut | ErrorKind::WouldBlock))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unpark(tid: ThreadId, _hint: usize) {
|
||||||
|
let _ = usercalls::send(EV_UNPARK, Some(tid));
|
||||||
|
}
|
||||||
@@ -40,7 +40,7 @@ pub mod stdio;
|
|||||||
pub mod thread;
|
pub mod thread;
|
||||||
pub mod thread_local_dtor;
|
pub mod thread_local_dtor;
|
||||||
pub mod thread_local_key;
|
pub mod thread_local_key;
|
||||||
pub mod thread_parker;
|
pub mod thread_parking;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
#[cfg(target_os = "espidf")]
|
#[cfg(target_os = "espidf")]
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
use crate::ffi::{c_int, c_void};
|
|
||||||
use crate::pin::Pin;
|
|
||||||
use crate::ptr::{null, null_mut};
|
|
||||||
use crate::sync::atomic::{
|
|
||||||
AtomicU64,
|
|
||||||
Ordering::{Acquire, Relaxed, Release},
|
|
||||||
};
|
|
||||||
use crate::time::Duration;
|
|
||||||
use libc::{_lwp_self, clockid_t, lwpid_t, time_t, timespec, CLOCK_MONOTONIC};
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
fn ___lwp_park60(
|
|
||||||
clock_id: clockid_t,
|
|
||||||
flags: c_int,
|
|
||||||
ts: *mut timespec,
|
|
||||||
unpark: lwpid_t,
|
|
||||||
hint: *const c_void,
|
|
||||||
unparkhint: *const c_void,
|
|
||||||
) -> c_int;
|
|
||||||
fn _lwp_unpark(lwp: lwpid_t, hint: *const c_void) -> c_int;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The thread is not parked and the token is not available.
|
|
||||||
///
|
|
||||||
/// Zero cannot be a valid LWP id, since it is used as empty value for the unpark
|
|
||||||
/// argument in _lwp_park.
|
|
||||||
const EMPTY: u64 = 0;
|
|
||||||
/// The token is available. Do not park anymore.
|
|
||||||
const NOTIFIED: u64 = u64::MAX;
|
|
||||||
|
|
||||||
pub struct Parker {
|
|
||||||
/// The parker state. Contains either one of the two state values above or the LWP
|
|
||||||
/// id of the parked thread.
|
|
||||||
state: AtomicU64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parker {
|
|
||||||
pub unsafe fn new(parker: *mut Parker) {
|
|
||||||
parker.write(Parker { state: AtomicU64::new(EMPTY) })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does not actually need `unsafe` or `Pin`, but the pthread implementation does.
|
|
||||||
pub unsafe fn park(self: Pin<&Self>) {
|
|
||||||
// If the token has already been made available, we can skip
|
|
||||||
// a bit of work, so check for it here.
|
|
||||||
if self.state.load(Acquire) != NOTIFIED {
|
|
||||||
let parked = _lwp_self() as u64;
|
|
||||||
let hint = self.state.as_mut_ptr().cast();
|
|
||||||
if self.state.compare_exchange(EMPTY, parked, Relaxed, Acquire).is_ok() {
|
|
||||||
// Loop to guard against spurious wakeups.
|
|
||||||
loop {
|
|
||||||
___lwp_park60(0, 0, null_mut(), 0, hint, null());
|
|
||||||
if self.state.load(Acquire) == NOTIFIED {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, the change to NOTIFIED has always been observed with acquire
|
|
||||||
// ordering, so we can just use a relaxed store here (instead of a swap).
|
|
||||||
self.state.store(EMPTY, Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does not actually need `unsafe` or `Pin`, but the pthread implementation does.
|
|
||||||
pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
|
|
||||||
if self.state.load(Acquire) != NOTIFIED {
|
|
||||||
let parked = _lwp_self() as u64;
|
|
||||||
let hint = self.state.as_mut_ptr().cast();
|
|
||||||
let mut timeout = timespec {
|
|
||||||
// Saturate so that the operation will definitely time out
|
|
||||||
// (even if it is after the heat death of the universe).
|
|
||||||
tv_sec: dur.as_secs().try_into().ok().unwrap_or(time_t::MAX),
|
|
||||||
tv_nsec: dur.subsec_nanos().into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.state.compare_exchange(EMPTY, parked, Relaxed, Acquire).is_ok() {
|
|
||||||
// Timeout needs to be mutable since it is modified on NetBSD 9.0 and
|
|
||||||
// above.
|
|
||||||
___lwp_park60(CLOCK_MONOTONIC, 0, &mut timeout, 0, hint, null());
|
|
||||||
// Use a swap to get acquire ordering even if the token was set after
|
|
||||||
// the timeout occurred.
|
|
||||||
self.state.swap(EMPTY, Acquire);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.state.store(EMPTY, Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does not actually need `Pin`, but the pthread implementation does.
|
|
||||||
pub fn unpark(self: Pin<&Self>) {
|
|
||||||
let state = self.state.swap(NOTIFIED, Release);
|
|
||||||
if !matches!(state, EMPTY | NOTIFIED) {
|
|
||||||
let lwp = state as lwpid_t;
|
|
||||||
let hint = self.state.as_mut_ptr().cast();
|
|
||||||
|
|
||||||
// If the parking thread terminated and did not actually park, this will
|
|
||||||
// probably return an error, which is OK. In the worst case, another
|
|
||||||
// thread has received the same LWP id. It will then receive a spurious
|
|
||||||
// wakeup, but those are allowable per the API contract. The same reasoning
|
|
||||||
// applies if a timeout occurred before this call, but the state was not
|
|
||||||
// yet reset.
|
|
||||||
|
|
||||||
// SAFETY:
|
|
||||||
// The syscall has no invariants to hold. Only unsafe because it is an
|
|
||||||
// extern function.
|
|
||||||
unsafe {
|
|
||||||
_lwp_unpark(lwp, hint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -46,7 +46,7 @@ unsafe impl Sync for Parker {}
|
|||||||
unsafe impl Send for Parker {}
|
unsafe impl Send for Parker {}
|
||||||
|
|
||||||
impl Parker {
|
impl Parker {
|
||||||
pub unsafe fn new(parker: *mut Parker) {
|
pub unsafe fn new_in_place(parker: *mut Parker) {
|
||||||
let semaphore = dispatch_semaphore_create(0);
|
let semaphore = dispatch_semaphore_create(0);
|
||||||
assert!(
|
assert!(
|
||||||
!semaphore.is_null(),
|
!semaphore.is_null(),
|
||||||
@@ -24,7 +24,7 @@ cfg_if::cfg_if! {
|
|||||||
pub use darwin::Parker;
|
pub use darwin::Parker;
|
||||||
} else if #[cfg(target_os = "netbsd")] {
|
} else if #[cfg(target_os = "netbsd")] {
|
||||||
mod netbsd;
|
mod netbsd;
|
||||||
pub use netbsd::Parker;
|
pub use netbsd::{current, park, park_timeout, unpark, ThreadId};
|
||||||
} else {
|
} else {
|
||||||
mod pthread;
|
mod pthread;
|
||||||
pub use pthread::Parker;
|
pub use pthread::Parker;
|
||||||
52
library/std/src/sys/unix/thread_parking/netbsd.rs
Normal file
52
library/std/src/sys/unix/thread_parking/netbsd.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use crate::ffi::{c_int, c_void};
|
||||||
|
use crate::ptr;
|
||||||
|
use crate::time::Duration;
|
||||||
|
use libc::{_lwp_self, clockid_t, lwpid_t, time_t, timespec, CLOCK_MONOTONIC};
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn ___lwp_park60(
|
||||||
|
clock_id: clockid_t,
|
||||||
|
flags: c_int,
|
||||||
|
ts: *mut timespec,
|
||||||
|
unpark: lwpid_t,
|
||||||
|
hint: *const c_void,
|
||||||
|
unparkhint: *const c_void,
|
||||||
|
) -> c_int;
|
||||||
|
fn _lwp_unpark(lwp: lwpid_t, hint: *const c_void) -> c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ThreadId = lwpid_t;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn current() -> ThreadId {
|
||||||
|
unsafe { _lwp_self() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn park(hint: usize) {
|
||||||
|
unsafe {
|
||||||
|
___lwp_park60(0, 0, ptr::null_mut(), 0, ptr::invalid(hint), ptr::null());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn park_timeout(dur: Duration, hint: usize) {
|
||||||
|
let mut timeout = timespec {
|
||||||
|
// Saturate so that the operation will definitely time out
|
||||||
|
// (even if it is after the heat death of the universe).
|
||||||
|
tv_sec: dur.as_secs().try_into().ok().unwrap_or(time_t::MAX),
|
||||||
|
tv_nsec: dur.subsec_nanos().into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Timeout needs to be mutable since it is modified on NetBSD 9.0 and
|
||||||
|
// above.
|
||||||
|
unsafe {
|
||||||
|
___lwp_park60(CLOCK_MONOTONIC, 0, &mut timeout, 0, ptr::invalid(hint), ptr::null());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn unpark(tid: ThreadId, hint: usize) {
|
||||||
|
unsafe {
|
||||||
|
_lwp_unpark(tid, ptr::invalid(hint));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -99,7 +99,7 @@ impl Parker {
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// The constructed parker must never be moved.
|
/// The constructed parker must never be moved.
|
||||||
pub unsafe fn new(parker: *mut Parker) {
|
pub unsafe fn new_in_place(parker: *mut Parker) {
|
||||||
// Use the default mutex implementation to allow for simpler initialization.
|
// Use the default mutex implementation to allow for simpler initialization.
|
||||||
// This could lead to undefined behaviour when deadlocking. This is avoided
|
// This could lead to undefined behaviour when deadlocking. This is avoided
|
||||||
// by not deadlocking. Note in particular the unlocking operation before any
|
// by not deadlocking. Note in particular the unlocking operation before any
|
||||||
@@ -33,7 +33,7 @@ pub mod stdio;
|
|||||||
pub mod thread;
|
pub mod thread;
|
||||||
pub mod thread_local_dtor;
|
pub mod thread_local_dtor;
|
||||||
pub mod thread_local_key;
|
pub mod thread_local_key;
|
||||||
pub mod thread_parker;
|
pub mod thread_parking;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(not(target_vendor = "uwp"))] {
|
if #[cfg(not(target_vendor = "uwp"))] {
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ const NOTIFIED: i8 = 1;
|
|||||||
impl Parker {
|
impl Parker {
|
||||||
/// Construct the Windows parker. The UNIX parker implementation
|
/// Construct the Windows parker. The UNIX parker implementation
|
||||||
/// requires this to happen in-place.
|
/// requires this to happen in-place.
|
||||||
pub unsafe fn new(parker: *mut Parker) {
|
pub unsafe fn new_in_place(parker: *mut Parker) {
|
||||||
parker.write(Self { state: AtomicI8::new(EMPTY) });
|
parker.write(Self { state: AtomicI8::new(EMPTY) });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ pub mod process;
|
|||||||
pub mod thread;
|
pub mod thread;
|
||||||
pub mod thread_info;
|
pub mod thread_info;
|
||||||
pub mod thread_local_dtor;
|
pub mod thread_local_dtor;
|
||||||
pub mod thread_parker;
|
pub mod thread_parking;
|
||||||
pub mod wstr;
|
pub mod wstr;
|
||||||
pub mod wtf8;
|
pub mod wtf8;
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ pub struct Parker {
|
|||||||
impl Parker {
|
impl Parker {
|
||||||
/// Construct the futex parker. The UNIX parker implementation
|
/// Construct the futex parker. The UNIX parker implementation
|
||||||
/// requires this to happen in-place.
|
/// requires this to happen in-place.
|
||||||
pub unsafe fn new(parker: *mut Parker) {
|
pub unsafe fn new_in_place(parker: *mut Parker) {
|
||||||
parker.write(Self { state: AtomicU32::new(EMPTY) });
|
parker.write(Self { state: AtomicU32::new(EMPTY) });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ pub struct Parker {
|
|||||||
impl Parker {
|
impl Parker {
|
||||||
/// Construct the generic parker. The UNIX parker implementation
|
/// Construct the generic parker. The UNIX parker implementation
|
||||||
/// requires this to happen in-place.
|
/// requires this to happen in-place.
|
||||||
pub unsafe fn new(parker: *mut Parker) {
|
pub unsafe fn new_in_place(parker: *mut Parker) {
|
||||||
parker.write(Parker {
|
parker.write(Parker {
|
||||||
state: AtomicUsize::new(EMPTY),
|
state: AtomicUsize::new(EMPTY),
|
||||||
lock: Mutex::new(()),
|
lock: Mutex::new(()),
|
||||||
108
library/std/src/sys_common/thread_parking/id.rs
Normal file
108
library/std/src/sys_common/thread_parking/id.rs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
//! Thread parking using thread ids.
|
||||||
|
//!
|
||||||
|
//! Some platforms (notably NetBSD) have thread parking primitives whose semantics
|
||||||
|
//! match those offered by `thread::park`, with the difference that the thread to
|
||||||
|
//! be unparked is referenced by a platform-specific thread id. Since the thread
|
||||||
|
//! parker is constructed before that id is known, an atomic state variable is used
|
||||||
|
//! to manage the park state and propagate the thread id. This also avoids platform
|
||||||
|
//! calls in the case where `unpark` is called before `park`.
|
||||||
|
|
||||||
|
use crate::cell::UnsafeCell;
|
||||||
|
use crate::pin::Pin;
|
||||||
|
use crate::sync::atomic::{
|
||||||
|
fence, AtomicI8,
|
||||||
|
Ordering::{Acquire, Relaxed, Release},
|
||||||
|
};
|
||||||
|
use crate::sys::thread_parking::{current, park, park_timeout, unpark, ThreadId};
|
||||||
|
use crate::time::Duration;
|
||||||
|
|
||||||
|
pub struct Parker {
|
||||||
|
state: AtomicI8,
|
||||||
|
tid: UnsafeCell<Option<ThreadId>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const PARKED: i8 = -1;
|
||||||
|
const EMPTY: i8 = 0;
|
||||||
|
const NOTIFIED: i8 = 1;
|
||||||
|
|
||||||
|
impl Parker {
|
||||||
|
pub fn new() -> Parker {
|
||||||
|
Parker { state: AtomicI8::new(EMPTY), tid: UnsafeCell::new(None) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new thread parker. UNIX requires this to happen in-place.
|
||||||
|
pub unsafe fn new_in_place(parker: *mut Parker) {
|
||||||
|
parker.write(Parker::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// * must always be called from the same thread
|
||||||
|
/// * must be called before the state is set to PARKED
|
||||||
|
unsafe fn init_tid(&self) {
|
||||||
|
// The field is only ever written to from this thread, so we don't need
|
||||||
|
// synchronization to read it here.
|
||||||
|
if self.tid.get().read().is_none() {
|
||||||
|
// Because this point is only reached once, before the state is set
|
||||||
|
// to PARKED for the first time, the non-atomic write here can not
|
||||||
|
// conflict with reads by other threads.
|
||||||
|
self.tid.get().write(Some(current()));
|
||||||
|
// Ensure that the write can be observed by all threads reading the
|
||||||
|
// state. Synchronizes with the acquire barrier in `unpark`.
|
||||||
|
fence(Release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn park(self: Pin<&Self>) {
|
||||||
|
self.init_tid();
|
||||||
|
|
||||||
|
// Changes NOTIFIED to EMPTY and EMPTY to PARKED.
|
||||||
|
let mut state = self.state.fetch_sub(1, Acquire).wrapping_sub(1);
|
||||||
|
if state == PARKED {
|
||||||
|
// Loop to guard against spurious wakeups.
|
||||||
|
while state == PARKED {
|
||||||
|
park(self.state.as_mut_ptr().addr());
|
||||||
|
state = self.state.load(Acquire);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the state change has already been observed with acquire
|
||||||
|
// ordering, the state can be reset with a relaxed store instead
|
||||||
|
// of a swap.
|
||||||
|
self.state.store(EMPTY, Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
|
||||||
|
self.init_tid();
|
||||||
|
|
||||||
|
let state = self.state.fetch_sub(1, Acquire).wrapping_sub(1);
|
||||||
|
if state == PARKED {
|
||||||
|
park_timeout(dur, self.state.as_mut_ptr().addr());
|
||||||
|
// Swap to ensure that we observe all state changes with acquire
|
||||||
|
// ordering, even if the state has been changed after the timeout
|
||||||
|
// occured.
|
||||||
|
self.state.swap(EMPTY, Acquire);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unpark(self: Pin<&Self>) {
|
||||||
|
let state = self.state.swap(NOTIFIED, Release);
|
||||||
|
if state == PARKED {
|
||||||
|
// Synchronize with the release fence in `init_tid` to observe the
|
||||||
|
// write to `tid`.
|
||||||
|
fence(Acquire);
|
||||||
|
// # Safety
|
||||||
|
// The thread id is initialized before the state is set to `PARKED`
|
||||||
|
// for the first time and is not written to from that point on
|
||||||
|
// (negating the need for an atomic read).
|
||||||
|
let tid = unsafe { self.tid.get().read().unwrap_unchecked() };
|
||||||
|
// It is possible that the waiting thread woke up because of a timeout
|
||||||
|
// and terminated before this call is made. This call then returns an
|
||||||
|
// error or wakes up an unrelated thread. The platform API and
|
||||||
|
// environment does allow this, however.
|
||||||
|
unpark(tid, self.state.as_mut_ptr().addr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for Parker {}
|
||||||
|
unsafe impl Sync for Parker {}
|
||||||
@@ -11,13 +11,17 @@ cfg_if::cfg_if! {
|
|||||||
))] {
|
))] {
|
||||||
mod futex;
|
mod futex;
|
||||||
pub use futex::Parker;
|
pub use futex::Parker;
|
||||||
|
} else if #[cfg(any(
|
||||||
|
target_os = "netbsd",
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx"),
|
||||||
|
))] {
|
||||||
|
mod id;
|
||||||
|
pub use id::Parker;
|
||||||
} else if #[cfg(target_os = "solid_asp3")] {
|
} else if #[cfg(target_os = "solid_asp3")] {
|
||||||
mod wait_flag;
|
mod wait_flag;
|
||||||
pub use wait_flag::Parker;
|
pub use wait_flag::Parker;
|
||||||
} else if #[cfg(any(windows, target_family = "unix"))] {
|
} else if #[cfg(any(windows, target_family = "unix"))] {
|
||||||
pub use crate::sys::thread_parker::Parker;
|
pub use crate::sys::thread_parking::Parker;
|
||||||
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
|
|
||||||
pub use crate::sys::thread_parker::Parker;
|
|
||||||
} else {
|
} else {
|
||||||
mod generic;
|
mod generic;
|
||||||
pub use generic::Parker;
|
pub use generic::Parker;
|
||||||
@@ -41,7 +41,7 @@ pub struct Parker {
|
|||||||
impl Parker {
|
impl Parker {
|
||||||
/// Construct a parker for the current thread. The UNIX parker
|
/// Construct a parker for the current thread. The UNIX parker
|
||||||
/// implementation requires this to happen in-place.
|
/// implementation requires this to happen in-place.
|
||||||
pub unsafe fn new(parker: *mut Parker) {
|
pub unsafe fn new_in_place(parker: *mut Parker) {
|
||||||
parker.write(Parker { state: AtomicI8::new(EMPTY), wait_flag: WaitFlag::new() })
|
parker.write(Parker { state: AtomicI8::new(EMPTY), wait_flag: WaitFlag::new() })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ use crate::sync::Arc;
|
|||||||
use crate::sys::thread as imp;
|
use crate::sys::thread as imp;
|
||||||
use crate::sys_common::thread;
|
use crate::sys_common::thread;
|
||||||
use crate::sys_common::thread_info;
|
use crate::sys_common::thread_info;
|
||||||
use crate::sys_common::thread_parker::Parker;
|
use crate::sys_common::thread_parking::Parker;
|
||||||
use crate::sys_common::{AsInner, IntoInner};
|
use crate::sys_common::{AsInner, IntoInner};
|
||||||
use crate::time::Duration;
|
use crate::time::Duration;
|
||||||
|
|
||||||
@@ -1216,7 +1216,7 @@ impl Thread {
|
|||||||
let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
|
let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
|
||||||
addr_of_mut!((*ptr).name).write(name);
|
addr_of_mut!((*ptr).name).write(name);
|
||||||
addr_of_mut!((*ptr).id).write(ThreadId::new());
|
addr_of_mut!((*ptr).id).write(ThreadId::new());
|
||||||
Parker::new(addr_of_mut!((*ptr).parker));
|
Parker::new_in_place(addr_of_mut!((*ptr).parker));
|
||||||
Pin::new_unchecked(arc.assume_init())
|
Pin::new_unchecked(arc.assume_init())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -97,18 +97,36 @@ impl RunConfig<'_> {
|
|||||||
self.builder.build.build
|
self.builder.build.build
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a `-p=x -p=y` string suitable for passing to a cargo invocation.
|
/// Return a list of crate names selected by `run.paths`.
|
||||||
pub fn cargo_crates_in_set(&self) -> Interned<Vec<String>> {
|
pub fn cargo_crates_in_set(&self) -> Interned<Vec<String>> {
|
||||||
let mut crates = Vec::new();
|
let mut crates = Vec::new();
|
||||||
for krate in &self.paths {
|
for krate in &self.paths {
|
||||||
let path = krate.assert_single_path();
|
let path = krate.assert_single_path();
|
||||||
let crate_name = self.builder.crate_paths[&path.path];
|
let crate_name = self.builder.crate_paths[&path.path];
|
||||||
crates.push(format!("-p={crate_name}"));
|
crates.push(crate_name.to_string());
|
||||||
}
|
}
|
||||||
INTERNER.intern_list(crates)
|
INTERNER.intern_list(crates)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A description of the crates in this set, suitable for passing to `builder.info`.
|
||||||
|
///
|
||||||
|
/// `crates` should be generated by [`RunConfig::cargo_crates_in_set`].
|
||||||
|
pub fn crate_description(crates: &[impl AsRef<str>]) -> String {
|
||||||
|
if crates.is_empty() {
|
||||||
|
return "".into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut descr = String::from(" {");
|
||||||
|
descr.push_str(crates[0].as_ref());
|
||||||
|
for krate in &crates[1..] {
|
||||||
|
descr.push_str(", ");
|
||||||
|
descr.push_str(krate.as_ref());
|
||||||
|
}
|
||||||
|
descr.push('}');
|
||||||
|
descr
|
||||||
|
}
|
||||||
|
|
||||||
struct StepDescription {
|
struct StepDescription {
|
||||||
default: bool,
|
default: bool,
|
||||||
only_hosts: bool,
|
only_hosts: bool,
|
||||||
|
|||||||
@@ -99,19 +99,13 @@ impl Step for Std {
|
|||||||
cargo_subcommand(builder.kind),
|
cargo_subcommand(builder.kind),
|
||||||
);
|
);
|
||||||
std_cargo(builder, target, compiler.stage, &mut cargo);
|
std_cargo(builder, target, compiler.stage, &mut cargo);
|
||||||
|
cargo.args(args(builder));
|
||||||
|
|
||||||
builder.info(&format!(
|
builder.info(&format!(
|
||||||
"Checking stage{} std artifacts ({} -> {})",
|
"Checking stage{} library artifacts ({} -> {})",
|
||||||
builder.top_stage, &compiler.host, target
|
builder.top_stage, &compiler.host, target
|
||||||
));
|
));
|
||||||
run_cargo(
|
run_cargo(builder, cargo, &libstd_stamp(builder, compiler, target), vec![], true);
|
||||||
builder,
|
|
||||||
cargo,
|
|
||||||
args(builder),
|
|
||||||
&libstd_stamp(builder, compiler, target),
|
|
||||||
vec![],
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
// We skip populating the sysroot in non-zero stage because that'll lead
|
// We skip populating the sysroot in non-zero stage because that'll lead
|
||||||
// to rlib/rmeta conflicts if std gets built during this session.
|
// to rlib/rmeta conflicts if std gets built during this session.
|
||||||
@@ -155,19 +149,13 @@ impl Step for Std {
|
|||||||
for krate in builder.in_tree_crates("test", Some(target)) {
|
for krate in builder.in_tree_crates("test", Some(target)) {
|
||||||
cargo.arg("-p").arg(krate.name);
|
cargo.arg("-p").arg(krate.name);
|
||||||
}
|
}
|
||||||
|
cargo.args(args(builder));
|
||||||
|
|
||||||
builder.info(&format!(
|
builder.info(&format!(
|
||||||
"Checking stage{} std test/bench/example targets ({} -> {})",
|
"Checking stage{} library test/bench/example targets ({} -> {})",
|
||||||
builder.top_stage, &compiler.host, target
|
builder.top_stage, &compiler.host, target
|
||||||
));
|
));
|
||||||
run_cargo(
|
run_cargo(builder, cargo, &libstd_test_stamp(builder, compiler, target), vec![], true);
|
||||||
builder,
|
|
||||||
cargo,
|
|
||||||
args(builder),
|
|
||||||
&libstd_test_stamp(builder, compiler, target),
|
|
||||||
vec![],
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,19 +219,13 @@ impl Step for Rustc {
|
|||||||
for krate in builder.in_tree_crates("rustc-main", Some(target)) {
|
for krate in builder.in_tree_crates("rustc-main", Some(target)) {
|
||||||
cargo.arg("-p").arg(krate.name);
|
cargo.arg("-p").arg(krate.name);
|
||||||
}
|
}
|
||||||
|
cargo.args(args(builder));
|
||||||
|
|
||||||
builder.info(&format!(
|
builder.info(&format!(
|
||||||
"Checking stage{} compiler artifacts ({} -> {})",
|
"Checking stage{} compiler artifacts ({} -> {})",
|
||||||
builder.top_stage, &compiler.host, target
|
builder.top_stage, &compiler.host, target
|
||||||
));
|
));
|
||||||
run_cargo(
|
run_cargo(builder, cargo, &librustc_stamp(builder, compiler, target), vec![], true);
|
||||||
builder,
|
|
||||||
cargo,
|
|
||||||
args(builder),
|
|
||||||
&librustc_stamp(builder, compiler, target),
|
|
||||||
vec![],
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
let libdir = builder.sysroot_libdir(compiler, target);
|
let libdir = builder.sysroot_libdir(compiler, target);
|
||||||
let hostdir = builder.sysroot_libdir(compiler, compiler.host);
|
let hostdir = builder.sysroot_libdir(compiler, compiler.host);
|
||||||
@@ -290,6 +272,7 @@ impl Step for CodegenBackend {
|
|||||||
.arg("--manifest-path")
|
.arg("--manifest-path")
|
||||||
.arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend)));
|
.arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend)));
|
||||||
rustc_cargo_env(builder, &mut cargo, target);
|
rustc_cargo_env(builder, &mut cargo, target);
|
||||||
|
cargo.args(args(builder));
|
||||||
|
|
||||||
builder.info(&format!(
|
builder.info(&format!(
|
||||||
"Checking stage{} {} artifacts ({} -> {})",
|
"Checking stage{} {} artifacts ({} -> {})",
|
||||||
@@ -299,7 +282,6 @@ impl Step for CodegenBackend {
|
|||||||
run_cargo(
|
run_cargo(
|
||||||
builder,
|
builder,
|
||||||
cargo,
|
cargo,
|
||||||
args(builder),
|
|
||||||
&codegen_backend_stamp(builder, compiler, target, backend),
|
&codegen_backend_stamp(builder, compiler, target, backend),
|
||||||
vec![],
|
vec![],
|
||||||
true,
|
true,
|
||||||
@@ -355,11 +337,13 @@ impl Step for RustAnalyzer {
|
|||||||
cargo.arg("--benches");
|
cargo.arg("--benches");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cargo.args(args(builder));
|
||||||
|
|
||||||
builder.info(&format!(
|
builder.info(&format!(
|
||||||
"Checking stage{} {} artifacts ({} -> {})",
|
"Checking stage{} {} artifacts ({} -> {})",
|
||||||
compiler.stage, "rust-analyzer", &compiler.host.triple, target.triple
|
compiler.stage, "rust-analyzer", &compiler.host.triple, target.triple
|
||||||
));
|
));
|
||||||
run_cargo(builder, cargo, args(builder), &stamp(builder, compiler, target), vec![], true);
|
run_cargo(builder, cargo, &stamp(builder, compiler, target), vec![], true);
|
||||||
|
|
||||||
/// Cargo's output path in a given stage, compiled by a particular
|
/// Cargo's output path in a given stage, compiled by a particular
|
||||||
/// compiler for the specified target.
|
/// compiler for the specified target.
|
||||||
@@ -413,6 +397,8 @@ macro_rules! tool_check_step {
|
|||||||
cargo.arg("--all-targets");
|
cargo.arg("--all-targets");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cargo.args(args(builder));
|
||||||
|
|
||||||
// Enable internal lints for clippy and rustdoc
|
// Enable internal lints for clippy and rustdoc
|
||||||
// NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]`
|
// NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]`
|
||||||
// See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776
|
// See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776
|
||||||
@@ -428,7 +414,6 @@ macro_rules! tool_check_step {
|
|||||||
run_cargo(
|
run_cargo(
|
||||||
builder,
|
builder,
|
||||||
cargo,
|
cargo,
|
||||||
args(builder),
|
|
||||||
&stamp(builder, compiler, target),
|
&stamp(builder, compiler, target),
|
||||||
vec![],
|
vec![],
|
||||||
true,
|
true,
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ use std::fs;
|
|||||||
use std::io::{self, ErrorKind};
|
use std::io::{self, ErrorKind};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::builder::{Builder, RunConfig, ShouldRun, Step};
|
use crate::builder::{crate_description, Builder, RunConfig, ShouldRun, Step};
|
||||||
use crate::cache::Interned;
|
use crate::cache::Interned;
|
||||||
use crate::config::TargetSelection;
|
|
||||||
use crate::util::t;
|
use crate::util::t;
|
||||||
use crate::{Build, Mode, Subcommand};
|
use crate::{Build, Compiler, Mode, Subcommand};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct CleanAll {}
|
pub struct CleanAll {}
|
||||||
@@ -40,7 +39,7 @@ macro_rules! clean_crate_tree {
|
|||||||
( $( $name:ident, $mode:path, $root_crate:literal);+ $(;)? ) => { $(
|
( $( $name:ident, $mode:path, $root_crate:literal);+ $(;)? ) => { $(
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct $name {
|
pub struct $name {
|
||||||
target: TargetSelection,
|
compiler: Compiler,
|
||||||
crates: Interned<Vec<String>>,
|
crates: Interned<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,22 +53,21 @@ macro_rules! clean_crate_tree {
|
|||||||
|
|
||||||
fn make_run(run: RunConfig<'_>) {
|
fn make_run(run: RunConfig<'_>) {
|
||||||
let builder = run.builder;
|
let builder = run.builder;
|
||||||
if builder.top_stage != 0 {
|
let compiler = builder.compiler(builder.top_stage, run.target);
|
||||||
panic!("non-stage-0 clean not supported for individual crates");
|
builder.ensure(Self { crates: run.cargo_crates_in_set(), compiler });
|
||||||
}
|
|
||||||
builder.ensure(Self { crates: run.cargo_crates_in_set(), target: run.target });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(self, builder: &Builder<'_>) -> Self::Output {
|
fn run(self, builder: &Builder<'_>) -> Self::Output {
|
||||||
let compiler = builder.compiler(0, self.target);
|
let compiler = self.compiler;
|
||||||
let mut cargo = builder.bare_cargo(compiler, $mode, self.target, "clean");
|
let target = compiler.host;
|
||||||
|
let mut cargo = builder.bare_cargo(compiler, $mode, target, "clean");
|
||||||
for krate in &*self.crates {
|
for krate in &*self.crates {
|
||||||
cargo.arg(krate);
|
cargo.arg(krate);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.info(&format!(
|
builder.info(&format!(
|
||||||
"Cleaning stage{} {} artifacts ({} -> {})",
|
"Cleaning{} stage{} {} artifacts ({} -> {})",
|
||||||
compiler.stage, stringify!($name).to_lowercase(), &compiler.host, self.target
|
crate_description(&self.crates), compiler.stage, stringify!($name).to_lowercase(), &compiler.host, target,
|
||||||
));
|
));
|
||||||
|
|
||||||
// NOTE: doesn't use `run_cargo` because we don't want to save a stamp file,
|
// NOTE: doesn't use `run_cargo` because we don't want to save a stamp file,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use std::str;
|
|||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::builder::crate_description;
|
||||||
use crate::builder::Cargo;
|
use crate::builder::Cargo;
|
||||||
use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
|
use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
|
||||||
use crate::cache::{Interned, INTERNER};
|
use crate::cache::{Interned, INTERNER};
|
||||||
@@ -110,7 +111,10 @@ impl Step for Std {
|
|||||||
let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
|
let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
|
||||||
if compiler_to_use != compiler {
|
if compiler_to_use != compiler {
|
||||||
builder.ensure(Std::new(compiler_to_use, target));
|
builder.ensure(Std::new(compiler_to_use, target));
|
||||||
builder.info(&format!("Uplifting stage1 std ({} -> {})", compiler_to_use.host, target));
|
builder.info(&format!(
|
||||||
|
"Uplifting stage1 library ({} -> {})",
|
||||||
|
compiler_to_use.host, target
|
||||||
|
));
|
||||||
|
|
||||||
// Even if we're not building std this stage, the new sysroot must
|
// Even if we're not building std this stage, the new sysroot must
|
||||||
// still contain the third party objects needed by various targets.
|
// still contain the third party objects needed by various targets.
|
||||||
@@ -126,19 +130,18 @@ impl Step for Std {
|
|||||||
|
|
||||||
let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "build");
|
let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "build");
|
||||||
std_cargo(builder, target, compiler.stage, &mut cargo);
|
std_cargo(builder, target, compiler.stage, &mut cargo);
|
||||||
|
for krate in &*self.crates {
|
||||||
|
cargo.arg("-p").arg(krate);
|
||||||
|
}
|
||||||
|
|
||||||
builder.info(&format!(
|
builder.info(&format!(
|
||||||
"Building stage{} std artifacts ({} -> {})",
|
"Building{} stage{} library artifacts ({} -> {})",
|
||||||
compiler.stage, &compiler.host, target
|
crate_description(&self.crates),
|
||||||
|
compiler.stage,
|
||||||
|
&compiler.host,
|
||||||
|
target,
|
||||||
));
|
));
|
||||||
run_cargo(
|
run_cargo(builder, cargo, &libstd_stamp(builder, compiler, target), target_deps, false);
|
||||||
builder,
|
|
||||||
cargo,
|
|
||||||
self.crates.to_vec(),
|
|
||||||
&libstd_stamp(builder, compiler, target),
|
|
||||||
target_deps,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
builder.ensure(StdLink::from_std(
|
builder.ensure(StdLink::from_std(
|
||||||
self,
|
self,
|
||||||
@@ -425,7 +428,7 @@ impl Step for StdLink {
|
|||||||
let target_compiler = self.target_compiler;
|
let target_compiler = self.target_compiler;
|
||||||
let target = self.target;
|
let target = self.target;
|
||||||
builder.info(&format!(
|
builder.info(&format!(
|
||||||
"Copying stage{} std from stage{} ({} -> {} / {})",
|
"Copying stage{} library from stage{} ({} -> {} / {})",
|
||||||
target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, target
|
target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, target
|
||||||
));
|
));
|
||||||
let libdir = builder.sysroot_libdir(target_compiler, target);
|
let libdir = builder.sysroot_libdir(target_compiler, target);
|
||||||
@@ -714,18 +717,18 @@ impl Step for Rustc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for krate in &*self.crates {
|
||||||
|
cargo.arg("-p").arg(krate);
|
||||||
|
}
|
||||||
|
|
||||||
builder.info(&format!(
|
builder.info(&format!(
|
||||||
"Building stage{} compiler artifacts ({} -> {})",
|
"Building{} stage{} compiler artifacts ({} -> {})",
|
||||||
compiler.stage, &compiler.host, target
|
crate_description(&self.crates),
|
||||||
|
compiler.stage,
|
||||||
|
&compiler.host,
|
||||||
|
target,
|
||||||
));
|
));
|
||||||
run_cargo(
|
run_cargo(builder, cargo, &librustc_stamp(builder, compiler, target), vec![], false);
|
||||||
builder,
|
|
||||||
cargo,
|
|
||||||
self.crates.to_vec(),
|
|
||||||
&librustc_stamp(builder, compiler, target),
|
|
||||||
vec![],
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
builder.ensure(RustcLink::from_rustc(
|
builder.ensure(RustcLink::from_rustc(
|
||||||
self,
|
self,
|
||||||
@@ -981,7 +984,7 @@ impl Step for CodegenBackend {
|
|||||||
"Building stage{} codegen backend {} ({} -> {})",
|
"Building stage{} codegen backend {} ({} -> {})",
|
||||||
compiler.stage, backend, &compiler.host, target
|
compiler.stage, backend, &compiler.host, target
|
||||||
));
|
));
|
||||||
let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false);
|
let files = run_cargo(builder, cargo, &tmp_stamp, vec![], false);
|
||||||
if builder.config.dry_run() {
|
if builder.config.dry_run() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1405,7 +1408,6 @@ pub fn add_to_sysroot(
|
|||||||
pub fn run_cargo(
|
pub fn run_cargo(
|
||||||
builder: &Builder<'_>,
|
builder: &Builder<'_>,
|
||||||
cargo: Cargo,
|
cargo: Cargo,
|
||||||
tail_args: Vec<String>,
|
|
||||||
stamp: &Path,
|
stamp: &Path,
|
||||||
additional_target_deps: Vec<(PathBuf, DependencyType)>,
|
additional_target_deps: Vec<(PathBuf, DependencyType)>,
|
||||||
is_check: bool,
|
is_check: bool,
|
||||||
@@ -1431,7 +1433,7 @@ pub fn run_cargo(
|
|||||||
// files we need to probe for later.
|
// files we need to probe for later.
|
||||||
let mut deps = Vec::new();
|
let mut deps = Vec::new();
|
||||||
let mut toplevel = Vec::new();
|
let mut toplevel = Vec::new();
|
||||||
let ok = stream_cargo(builder, cargo, tail_args, &mut |msg| {
|
let ok = stream_cargo(builder, cargo, &mut |msg| {
|
||||||
let (filenames, crate_types) = match msg {
|
let (filenames, crate_types) = match msg {
|
||||||
CargoMessage::CompilerArtifact {
|
CargoMessage::CompilerArtifact {
|
||||||
filenames,
|
filenames,
|
||||||
@@ -1546,7 +1548,6 @@ pub fn run_cargo(
|
|||||||
pub fn stream_cargo(
|
pub fn stream_cargo(
|
||||||
builder: &Builder<'_>,
|
builder: &Builder<'_>,
|
||||||
cargo: Cargo,
|
cargo: Cargo,
|
||||||
tail_args: Vec<String>,
|
|
||||||
cb: &mut dyn FnMut(CargoMessage<'_>),
|
cb: &mut dyn FnMut(CargoMessage<'_>),
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut cargo = Command::from(cargo);
|
let mut cargo = Command::from(cargo);
|
||||||
@@ -1566,10 +1567,6 @@ pub fn stream_cargo(
|
|||||||
}
|
}
|
||||||
cargo.arg("--message-format").arg(message_format).stdout(Stdio::piped());
|
cargo.arg("--message-format").arg(message_format).stdout(Stdio::piped());
|
||||||
|
|
||||||
for arg in tail_args {
|
|
||||||
cargo.arg(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.verbose(&format!("running: {:?}", cargo));
|
builder.verbose(&format!("running: {:?}", cargo));
|
||||||
let mut child = match cargo.spawn() {
|
let mut child = match cargo.spawn() {
|
||||||
Ok(child) => child,
|
Ok(child) => child,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use std::fs;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use crate::builder::crate_description;
|
||||||
use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
|
use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
|
||||||
use crate::cache::{Interned, INTERNER};
|
use crate::cache::{Interned, INTERNER};
|
||||||
use crate::compile;
|
use crate::compile;
|
||||||
@@ -558,7 +559,8 @@ fn doc_std(
|
|||||||
requested_crates: &[String],
|
requested_crates: &[String],
|
||||||
) {
|
) {
|
||||||
builder.info(&format!(
|
builder.info(&format!(
|
||||||
"Documenting stage{} std ({}) in {} format",
|
"Documenting{} stage{} library ({}) in {} format",
|
||||||
|
crate_description(requested_crates),
|
||||||
stage,
|
stage,
|
||||||
target,
|
target,
|
||||||
format.as_str()
|
format.as_str()
|
||||||
|
|||||||
@@ -352,32 +352,32 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
|
|||||||
|
|
||||||
// fn usage()
|
// fn usage()
|
||||||
let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! {
|
let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! {
|
||||||
// We have an unfortunate situation here: some Steps use `builder.in_tree_crates` to determine their paths.
|
|
||||||
// To determine those crates, we need to run `cargo metadata`, which means we need all submodules to be checked out.
|
|
||||||
// That takes a while to run, so only do it when paths were explicitly requested, not on all CLI errors.
|
|
||||||
// `Build::new` won't load submodules for the `setup` command.
|
|
||||||
let cmd = if verbose {
|
|
||||||
println!("note: updating submodules before printing available paths");
|
|
||||||
"build"
|
|
||||||
} else {
|
|
||||||
"setup"
|
|
||||||
};
|
|
||||||
let config = Config::parse(&[cmd.to_string()]);
|
|
||||||
let build = Build::new(config);
|
|
||||||
let paths = Builder::get_help(&build, subcommand);
|
|
||||||
|
|
||||||
println!("{}", opts.usage(subcommand_help));
|
println!("{}", opts.usage(subcommand_help));
|
||||||
if let Some(s) = paths {
|
if verbose {
|
||||||
if verbose {
|
// We have an unfortunate situation here: some Steps use `builder.in_tree_crates` to determine their paths.
|
||||||
|
// To determine those crates, we need to run `cargo metadata`, which means we need all submodules to be checked out.
|
||||||
|
// That takes a while to run, so only do it when paths were explicitly requested, not on all CLI errors.
|
||||||
|
// `Build::new` won't load submodules for the `setup` command.
|
||||||
|
let cmd = if verbose {
|
||||||
|
println!("note: updating submodules before printing available paths");
|
||||||
|
"build"
|
||||||
|
} else {
|
||||||
|
"setup"
|
||||||
|
};
|
||||||
|
let config = Config::parse(&[cmd.to_string()]);
|
||||||
|
let build = Build::new(config);
|
||||||
|
let paths = Builder::get_help(&build, subcommand);
|
||||||
|
|
||||||
|
if let Some(s) = paths {
|
||||||
println!("{}", s);
|
println!("{}", s);
|
||||||
} else {
|
} else {
|
||||||
println!(
|
panic!("No paths available for subcommand `{}`", subcommand.as_str());
|
||||||
"Run `./x.py {} -h -v` to see a list of available paths.",
|
|
||||||
subcommand.as_str()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else if verbose {
|
} else {
|
||||||
panic!("No paths available for subcommand `{}`", subcommand.as_str());
|
println!(
|
||||||
|
"Run `./x.py {} -h -v` to see a list of available paths.",
|
||||||
|
subcommand.as_str()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
crate::detail_exit(exit_code);
|
crate::detail_exit(exit_code);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -79,24 +79,19 @@ fn update_rustfmt_version(build: &Builder<'_>) {
|
|||||||
///
|
///
|
||||||
/// Returns `None` if all files should be formatted.
|
/// Returns `None` if all files should be formatted.
|
||||||
fn get_modified_rs_files(build: &Builder<'_>) -> Option<Vec<String>> {
|
fn get_modified_rs_files(build: &Builder<'_>) -> Option<Vec<String>> {
|
||||||
let Ok(remote) = get_rust_lang_rust_remote() else {return None;};
|
let Ok(remote) = get_rust_lang_rust_remote() else { return None; };
|
||||||
if !verify_rustfmt_version(build) {
|
if !verify_rustfmt_version(build) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let merge_base =
|
||||||
|
output(build.config.git().arg("merge-base").arg(&format!("{remote}/master")).arg("HEAD"));
|
||||||
Some(
|
Some(
|
||||||
output(
|
output(build.config.git().arg("diff-index").arg("--name-only").arg(merge_base.trim()))
|
||||||
build
|
.lines()
|
||||||
.config
|
.map(|s| s.trim().to_owned())
|
||||||
.git()
|
.filter(|f| Path::new(f).extension().map_or(false, |ext| ext == "rs"))
|
||||||
.arg("diff-index")
|
.collect(),
|
||||||
.arg("--name-only")
|
|
||||||
.arg("--merge-base")
|
|
||||||
.arg(&format!("{remote}/master")),
|
|
||||||
)
|
|
||||||
.lines()
|
|
||||||
.map(|s| s.trim().to_owned())
|
|
||||||
.filter(|f| Path::new(f).extension().map_or(false, |ext| ext == "rs"))
|
|
||||||
.collect(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use std::iter;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
|
use crate::builder::crate_description;
|
||||||
use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
|
use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
|
||||||
use crate::cache::Interned;
|
use crate::cache::Interned;
|
||||||
use crate::compile;
|
use crate::compile;
|
||||||
@@ -2154,8 +2155,12 @@ impl Step for Crate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
builder.info(&format!(
|
builder.info(&format!(
|
||||||
"{} {:?} stage{} ({} -> {})",
|
"{}{} stage{} ({} -> {})",
|
||||||
test_kind, self.crates, compiler.stage, &compiler.host, target
|
test_kind,
|
||||||
|
crate_description(&self.crates),
|
||||||
|
compiler.stage,
|
||||||
|
&compiler.host,
|
||||||
|
target
|
||||||
));
|
));
|
||||||
let _time = util::timeit(&builder);
|
let _time = util::timeit(&builder);
|
||||||
try_run(builder, &mut cargo.into());
|
try_run(builder, &mut cargo.into());
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ impl Step for ToolBuild {
|
|||||||
|
|
||||||
builder.info(&format!("Building stage{} tool {} ({})", compiler.stage, tool, target));
|
builder.info(&format!("Building stage{} tool {} ({})", compiler.stage, tool, target));
|
||||||
let mut duplicates = Vec::new();
|
let mut duplicates = Vec::new();
|
||||||
let is_expected = compile::stream_cargo(builder, cargo, vec![], &mut |msg| {
|
let is_expected = compile::stream_cargo(builder, cargo, &mut |msg| {
|
||||||
// Only care about big things like the RLS/Cargo for now
|
// Only care about big things like the RLS/Cargo for now
|
||||||
match tool {
|
match tool {
|
||||||
"rls" | "cargo" | "clippy-driver" | "miri" | "rustfmt" => {}
|
"rls" | "cargo" | "clippy-driver" | "miri" | "rustfmt" => {}
|
||||||
|
|||||||
@@ -131,6 +131,13 @@ pub struct Type13<'a> {
|
|||||||
member3: &'a Type13<'a>,
|
member3: &'a Type13<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper type to allow `Type14<Bar>` to be a unique ID
|
||||||
|
pub struct Bar;
|
||||||
|
|
||||||
|
// repr(transparent) parameterized type
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Type14<T>(T);
|
||||||
|
|
||||||
pub fn foo0(_: ()) { }
|
pub fn foo0(_: ()) { }
|
||||||
// CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]]
|
// CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]]
|
||||||
pub fn foo1(_: c_void, _: ()) { }
|
pub fn foo1(_: c_void, _: ()) { }
|
||||||
@@ -425,6 +432,12 @@ pub fn foo145(_: Type13, _: Type13) { }
|
|||||||
// CHECK: define{{.*}}foo145{{.*}}!type ![[TYPE145:[0-9]+]]
|
// CHECK: define{{.*}}foo145{{.*}}!type ![[TYPE145:[0-9]+]]
|
||||||
pub fn foo146(_: Type13, _: Type13, _: Type13) { }
|
pub fn foo146(_: Type13, _: Type13, _: Type13) { }
|
||||||
// CHECK: define{{.*}}foo146{{.*}}!type ![[TYPE146:[0-9]+]]
|
// CHECK: define{{.*}}foo146{{.*}}!type ![[TYPE146:[0-9]+]]
|
||||||
|
pub fn foo147(_: Type14<Bar>) { }
|
||||||
|
// CHECK: define{{.*}}foo147{{.*}}!type ![[TYPE147:[0-9]+]]
|
||||||
|
pub fn foo148(_: Type14<Bar>, _: Type14<Bar>) { }
|
||||||
|
// CHECK: define{{.*}}foo148{{.*}}!type ![[TYPE148:[0-9]+]]
|
||||||
|
pub fn foo149(_: Type14<Bar>, _: Type14<Bar>, _: Type14<Bar>) { }
|
||||||
|
// CHECK: define{{.*}}foo149{{.*}}!type ![[TYPE149:[0-9]+]]
|
||||||
|
|
||||||
// CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFvvE"}
|
// CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFvvE"}
|
||||||
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvvvE"}
|
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvvvE"}
|
||||||
@@ -570,6 +583,9 @@ pub fn foo146(_: Type13, _: Type13, _: Type13) { }
|
|||||||
// CHECK: ![[TYPE141]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3FooE"}
|
// CHECK: ![[TYPE141]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3FooE"}
|
||||||
// CHECK: ![[TYPE142]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3FooS_E"}
|
// CHECK: ![[TYPE142]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3FooS_E"}
|
||||||
// CHECK: ![[TYPE143]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3FooS_S_E"}
|
// CHECK: ![[TYPE143]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3FooS_S_E"}
|
||||||
// CHECK: ![[TYPE144]] = !{i64 0, !"_ZTSFvu3refIu3refIvEEE"}
|
// CHECK: ![[TYPE144]] = !{i64 0, !"_ZTSFvu3refIvEE"}
|
||||||
// CHECK: ![[TYPE145]] = !{i64 0, !"_ZTSFvu3refIu3refIvEES0_E"}
|
// CHECK: ![[TYPE145]] = !{i64 0, !"_ZTSFvu3refIvES_E"}
|
||||||
// CHECK: ![[TYPE146]] = !{i64 0, !"_ZTSFvu3refIu3refIvEES0_S0_E"}
|
// CHECK: ![[TYPE146]] = !{i64 0, !"_ZTSFvu3refIvES_S_E"}
|
||||||
|
// CHECK: ![[TYPE147]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarE
|
||||||
|
// CHECK: ![[TYPE148]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarS_E
|
||||||
|
// CHECK: ![[TYPE149]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarS_S_E
|
||||||
|
|||||||
@@ -33,3 +33,17 @@ assert-property: (
|
|||||||
".more-scraped-examples .scraped-example:nth-child(6) .code-wrapper .src-line-numbers",
|
".more-scraped-examples .scraped-example:nth-child(6) .code-wrapper .src-line-numbers",
|
||||||
{"clientWidth": |clientWidth|}
|
{"clientWidth": |clientWidth|}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Check that for both mobile and desktop sizes, the buttons in scraped examples are displayed
|
||||||
|
// correctly.
|
||||||
|
|
||||||
|
store-value: (offset_y, 4)
|
||||||
|
|
||||||
|
// First with desktop
|
||||||
|
assert-position: (".scraped-example .code-wrapper", {"y": 255})
|
||||||
|
assert-position: (".scraped-example .code-wrapper .prev", {"y": 255 + |offset_y|})
|
||||||
|
|
||||||
|
// Then with mobile
|
||||||
|
size: (600, 600)
|
||||||
|
assert-position: (".scraped-example .code-wrapper", {"y": 314})
|
||||||
|
assert-position: (".scraped-example .code-wrapper .prev", {"y": 314 + |offset_y|})
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
// check-pass
|
|
||||||
|
|
||||||
#![deny(multiple_supertrait_upcastable)]
|
|
||||||
//~^ WARNING unknown lint: `multiple_supertrait_upcastable`
|
|
||||||
//~| WARNING unknown lint: `multiple_supertrait_upcastable`
|
|
||||||
//~| WARNING unknown lint: `multiple_supertrait_upcastable`
|
|
||||||
#![warn(multiple_supertrait_upcastable)]
|
|
||||||
//~^ WARNING unknown lint: `multiple_supertrait_upcastable`
|
|
||||||
//~| WARNING unknown lint: `multiple_supertrait_upcastable`
|
|
||||||
//~| WARNING unknown lint: `multiple_supertrait_upcastable`
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
warning: unknown lint: `multiple_supertrait_upcastable`
|
|
||||||
--> $DIR/feature-gate-multiple_supertrait_upcastable.rs:3:1
|
|
||||||
|
|
|
||||||
LL | #![deny(multiple_supertrait_upcastable)]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
= note: the `multiple_supertrait_upcastable` lint is unstable
|
|
||||||
= help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
|
|
||||||
= note: `#[warn(unknown_lints)]` on by default
|
|
||||||
|
|
||||||
warning: unknown lint: `multiple_supertrait_upcastable`
|
|
||||||
--> $DIR/feature-gate-multiple_supertrait_upcastable.rs:7:1
|
|
||||||
|
|
|
||||||
LL | #![warn(multiple_supertrait_upcastable)]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
= note: the `multiple_supertrait_upcastable` lint is unstable
|
|
||||||
= help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
|
|
||||||
|
|
||||||
warning: unknown lint: `multiple_supertrait_upcastable`
|
|
||||||
--> $DIR/feature-gate-multiple_supertrait_upcastable.rs:3:1
|
|
||||||
|
|
|
||||||
LL | #![deny(multiple_supertrait_upcastable)]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
= note: the `multiple_supertrait_upcastable` lint is unstable
|
|
||||||
= help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
|
|
||||||
|
|
||||||
warning: unknown lint: `multiple_supertrait_upcastable`
|
|
||||||
--> $DIR/feature-gate-multiple_supertrait_upcastable.rs:7:1
|
|
||||||
|
|
|
||||||
LL | #![warn(multiple_supertrait_upcastable)]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
= note: the `multiple_supertrait_upcastable` lint is unstable
|
|
||||||
= help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
|
|
||||||
|
|
||||||
warning: unknown lint: `multiple_supertrait_upcastable`
|
|
||||||
--> $DIR/feature-gate-multiple_supertrait_upcastable.rs:3:1
|
|
||||||
|
|
|
||||||
LL | #![deny(multiple_supertrait_upcastable)]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
= note: the `multiple_supertrait_upcastable` lint is unstable
|
|
||||||
= help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
|
|
||||||
|
|
||||||
warning: unknown lint: `multiple_supertrait_upcastable`
|
|
||||||
--> $DIR/feature-gate-multiple_supertrait_upcastable.rs:7:1
|
|
||||||
|
|
|
||||||
LL | #![warn(multiple_supertrait_upcastable)]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
= note: the `multiple_supertrait_upcastable` lint is unstable
|
|
||||||
= help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
|
|
||||||
|
|
||||||
warning: 6 warnings emitted
|
|
||||||
|
|
||||||
9
src/test/ui/object-safety/issue-106247.rs
Normal file
9
src/test/ui/object-safety/issue-106247.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// check-pass
|
||||||
|
|
||||||
|
#![deny(where_clauses_object_safety)]
|
||||||
|
|
||||||
|
pub trait Trait {
|
||||||
|
fn method(&self) where Self: Sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#![feature(multiple_supertrait_upcastable)]
|
|
||||||
#![deny(multiple_supertrait_upcastable)]
|
|
||||||
|
|
||||||
trait A {}
|
|
||||||
trait B {}
|
|
||||||
|
|
||||||
trait C: A + B {}
|
|
||||||
//~^ ERROR `C` is object-safe and has multiple supertraits
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
error: `C` is object-safe and has multiple supertraits
|
|
||||||
--> $DIR/multiple_supertrait_upcastable.rs:7:1
|
|
||||||
|
|
|
||||||
LL | trait C: A + B {}
|
|
||||||
| ^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
note: the lint level is defined here
|
|
||||||
--> $DIR/multiple_supertrait_upcastable.rs:2:9
|
|
||||||
|
|
|
||||||
LL | #![deny(multiple_supertrait_upcastable)]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ miropt-test-tools = { path = "../miropt-test-tools" }
|
|||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
ignore = "0.4.18"
|
ignore = "0.4.18"
|
||||||
|
termcolor = "1.1.3"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "rust-tidy"
|
name = "rust-tidy"
|
||||||
|
|||||||
@@ -80,15 +80,6 @@ fn check_if_error_code_is_test_in_explanation(f: &str, err_code: &str) -> bool {
|
|||||||
ignore_found
|
ignore_found
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! some_or_continue {
|
|
||||||
($e:expr) => {
|
|
||||||
match $e {
|
|
||||||
Some(e) => e,
|
|
||||||
None => continue,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_error_codes(
|
fn extract_error_codes(
|
||||||
f: &str,
|
f: &str,
|
||||||
error_codes: &mut HashMap<String, ErrorCodeStatus>,
|
error_codes: &mut HashMap<String, ErrorCodeStatus>,
|
||||||
@@ -122,10 +113,16 @@ fn extract_error_codes(
|
|||||||
Some((file_name, _)) => file_name,
|
Some((file_name, _)) => file_name,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let path = some_or_continue!(path.parent())
|
|
||||||
|
let Some(parent) = path.parent() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let path = parent
|
||||||
.join(md_file_name)
|
.join(md_file_name)
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
.expect("failed to canonicalize error explanation file path");
|
.expect("failed to canonicalize error explanation file path");
|
||||||
|
|
||||||
match read_to_string(&path) {
|
match read_to_string(&path) {
|
||||||
Ok(content) => {
|
Ok(content) => {
|
||||||
let has_test = check_if_error_code_is_test_in_explanation(&content, &err_code);
|
let has_test = check_if_error_code_is_test_in_explanation(&content, &err_code);
|
||||||
@@ -215,8 +212,6 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
|
|||||||
// * #[error = "E0111"]
|
// * #[error = "E0111"]
|
||||||
let regex = Regex::new(r#"[(,"\s](E\d{4})[,)"]"#).unwrap();
|
let regex = Regex::new(r#"[(,"\s](E\d{4})[,)"]"#).unwrap();
|
||||||
|
|
||||||
println!("Checking which error codes lack tests...");
|
|
||||||
|
|
||||||
for path in paths {
|
for path in paths {
|
||||||
walk(path, &mut filter_dirs, &mut |entry, contents| {
|
walk(path, &mut filter_dirs, &mut |entry, contents| {
|
||||||
let file_name = entry.file_name();
|
let file_name = entry.file_name();
|
||||||
@@ -245,20 +240,15 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if found_explanations == 0 {
|
if found_explanations == 0 {
|
||||||
eprintln!("No error code explanation was tested!");
|
tidy_error!(bad, "No error code explanation was tested!");
|
||||||
*bad = true;
|
|
||||||
}
|
}
|
||||||
if found_tests == 0 {
|
if found_tests == 0 {
|
||||||
eprintln!("No error code was found in compilation errors!");
|
tidy_error!(bad, "No error code was found in compilation errors!");
|
||||||
*bad = true;
|
|
||||||
}
|
}
|
||||||
if explanations.is_empty() {
|
if explanations.is_empty() {
|
||||||
eprintln!("No error code explanation was found!");
|
tidy_error!(bad, "No error code explanation was found!");
|
||||||
*bad = true;
|
|
||||||
}
|
}
|
||||||
if errors.is_empty() {
|
if errors.is_empty() {
|
||||||
println!("Found {} error codes", error_codes.len());
|
|
||||||
|
|
||||||
for (err_code, error_status) in &error_codes {
|
for (err_code, error_status) in &error_codes {
|
||||||
if !error_status.has_test && !EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
|
if !error_status.has_test && !EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
|
||||||
errors.push(format!("Error code {err_code} needs to have at least one UI test!"));
|
errors.push(format!("Error code {err_code} needs to have at least one UI test!"));
|
||||||
@@ -310,11 +300,6 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
|
|||||||
}
|
}
|
||||||
errors.sort();
|
errors.sort();
|
||||||
for err in &errors {
|
for err in &errors {
|
||||||
eprintln!("{err}");
|
tidy_error!(bad, "{err}");
|
||||||
}
|
}
|
||||||
println!("Found {} error(s) in error codes", errors.len());
|
|
||||||
if !errors.is_empty() {
|
|
||||||
*bad = true;
|
|
||||||
}
|
|
||||||
println!("Done!");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
//! This library contains the tidy lints and exposes it
|
//! This library contains the tidy lints and exposes it
|
||||||
//! to be used by tools.
|
//! to be used by tools.
|
||||||
|
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use termcolor::WriteColor;
|
||||||
|
|
||||||
/// A helper macro to `unwrap` a result except also print out details like:
|
/// A helper macro to `unwrap` a result except also print out details like:
|
||||||
///
|
///
|
||||||
/// * The expression that failed
|
/// * The expression that failed
|
||||||
@@ -26,18 +30,27 @@ macro_rules! t {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! tidy_error {
|
macro_rules! tidy_error {
|
||||||
($bad:expr, $fmt:expr) => ({
|
($bad:expr, $($fmt:tt)*) => ({
|
||||||
*$bad = true;
|
$crate::tidy_error($bad, format_args!($($fmt)*)).expect("failed to output error");
|
||||||
eprint!("tidy error: ");
|
|
||||||
eprintln!($fmt);
|
|
||||||
});
|
|
||||||
($bad:expr, $fmt:expr, $($arg:tt)*) => ({
|
|
||||||
*$bad = true;
|
|
||||||
eprint!("tidy error: ");
|
|
||||||
eprintln!($fmt, $($arg)*);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tidy_error(bad: &mut bool, args: impl Display) -> std::io::Result<()> {
|
||||||
|
use std::io::Write;
|
||||||
|
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream};
|
||||||
|
|
||||||
|
*bad = true;
|
||||||
|
|
||||||
|
let mut stderr = StandardStream::stdout(ColorChoice::Auto);
|
||||||
|
stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
|
||||||
|
|
||||||
|
write!(&mut stderr, "tidy error")?;
|
||||||
|
stderr.set_color(&ColorSpec::new())?;
|
||||||
|
|
||||||
|
writeln!(&mut stderr, ": {args}")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub mod alphabetical;
|
pub mod alphabetical;
|
||||||
pub mod bins;
|
pub mod bins;
|
||||||
pub mod debug_artifacts;
|
pub mod debug_artifacts;
|
||||||
|
|||||||
Reference in New Issue
Block a user