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:
bors
2022-12-31 05:27:07 +00:00
49 changed files with 428 additions and 578 deletions

View File

@@ -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",
] ]

View File

@@ -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);
} }

View File

@@ -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);
} }
} }

View File

@@ -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.

View File

@@ -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,
] ]
] ]
); );

View File

@@ -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,
);
}
}
}
}

View File

@@ -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,

View File

@@ -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>>,

View File

@@ -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)]

View File

@@ -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.
/// ///

View File

@@ -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)]

View File

@@ -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;

View File

@@ -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))
} }
} }

View File

@@ -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));
}
}
}

View 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));
}

View File

@@ -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")]

View File

@@ -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);
}
}
}
}

View File

@@ -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(),

View File

@@ -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;

View 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));
}
}

View File

@@ -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

View File

@@ -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"))] {

View File

@@ -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) });
} }

View File

@@ -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;

View File

@@ -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) });
} }

View File

@@ -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(()),

View 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 {}

View File

@@ -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;

View File

@@ -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() })
} }

View File

@@ -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())
}; };

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View 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,

View File

@@ -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()

View File

@@ -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);
}; };

View File

@@ -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(),
) )
} }

View File

@@ -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());

View File

@@ -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" => {}

View File

@@ -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

View File

@@ -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|})

View File

@@ -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() {}

View File

@@ -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

View File

@@ -0,0 +1,9 @@
// check-pass
#![deny(where_clauses_object_safety)]
pub trait Trait {
fn method(&self) where Self: Sync;
}
fn main() {}

View File

@@ -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() {}

View File

@@ -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

View File

@@ -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"

View File

@@ -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!");
} }

View File

@@ -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;