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]]
name = "owo-colors"
version = "3.4.0"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "packed_simd_2"
@@ -5203,9 +5203,9 @@ dependencies = [
[[package]]
name = "termcolor"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
@@ -5309,6 +5309,7 @@ dependencies = [
"lazy_static",
"miropt-test-tools",
"regex",
"termcolor",
"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
// Don't do this for `ExplicitBug`, which has an unhelpful message and backtrace.
if !info.payload().is::<rustc_errors::ExplicitBug>() {
// Don't do this for `GoodPathBug`, which already emits its own more useful backtrace.
if !info.payload().is::<rustc_errors::GoodPathBug>() {
(*DEFAULT_HOOK)(info);
// 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
// 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");
handler.emit_diagnostic(&mut d);
}

View File

@@ -40,12 +40,13 @@ use rustc_span::source_map::SourceMap;
use rustc_span::HashStableContext;
use rustc_span::{Loc, Span};
use std::any::Any;
use std::borrow::Cow;
use std::fmt;
use std::hash::Hash;
use std::num::NonZeroUsize;
use std::panic;
use std::path::Path;
use std::{error, fmt};
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`
/// or `.span_bug` rather than a failed assertion, etc.
#[derive(Copy, Clone, Debug)]
pub struct ExplicitBug;
impl fmt::Display for ExplicitBug {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "parser internal bug")
}
}
impl error::Error for ExplicitBug {}
/// Signifies that the compiler died with an explicit call to `.delay_good_path_bug`
/// rather than a failed assertion, etc.
pub struct GoodPathBug;
pub use diagnostic::{
AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId,
@@ -507,7 +503,11 @@ impl Drop for HandlerInner {
if !self.has_errors() {
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!
@@ -520,6 +520,7 @@ impl Drop for HandlerInner {
self.flush_delayed(
bugs.into_iter().map(DelayedDiagnostic::decorate),
"no warnings or errors encountered even though `delayed_good_path_bugs` issued",
GoodPathBug,
);
}
@@ -1203,7 +1204,11 @@ impl Handler {
pub fn flush_delayed(&self) {
let mut inner = self.inner.lock();
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,
bugs: impl IntoIterator<Item = Diagnostic>,
explanation: impl Into<DiagnosticMessage> + Copy,
panic_with: impl Any + Send + 'static,
) {
let mut no_bugs = true;
for mut bug in bugs {
@@ -1607,7 +1613,7 @@ impl HandlerInner {
// Panic with `ExplicitBug` to avoid "unexpected panic" messages.
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),
/// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic.
(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]`.
(active, omit_gdb_pretty_printer_section, "1.5.0", None, None),
/// Allows using `#[prelude_import]` on glob `use` items.

View File

@@ -61,7 +61,6 @@ mod late;
mod let_underscore;
mod levels;
mod methods;
mod multiple_supertrait_upcastable;
mod non_ascii_idents;
mod non_fmt_panic;
mod nonstandard_style;
@@ -96,7 +95,6 @@ use hidden_unicode_codepoints::*;
use internal::*;
use let_underscore::*;
use methods::*;
use multiple_supertrait_upcastable::*;
use non_ascii_idents::*;
use non_fmt_panic::NonPanicFmt;
use nonstandard_style::*;
@@ -231,7 +229,6 @@ late_lint_methods!(
InvalidAtomicOrdering: InvalidAtomicOrdering,
NamedAsmLabels: NamedAsmLabels,
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_assign,
mul_with_overflow,
multiple_supertrait_upcastable,
must_not_suspend,
must_use,
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
/// Rust types that are not used at the FFI boundary.
#[instrument(level = "trace", skip(tcx, dict))]
fn encode_fnsig<'tcx>(
tcx: TyCtxt<'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
// c_void types into unit types unconditionally, and generalizes all pointers if
// 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> {
let mut ty = ty;
@@ -698,7 +700,7 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
!is_zst
});
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
// or reference, and either references itself or any other type that contains or
// 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
/// 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>(
tcx: TyCtxt<'tcx>,
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,

View File

@@ -87,7 +87,6 @@
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![allow(explicit_outlives_requirements)]
#![cfg_attr(not(bootstrap), warn(multiple_supertrait_upcastable))]
//
// Library features:
#![feature(alloc_layout_extra)]
@@ -192,7 +191,6 @@
#![feature(unsized_fn_params)]
#![feature(c_unwind)]
#![feature(with_negative_coherence)]
#![cfg_attr(not(bootstrap), feature(multiple_supertrait_upcastable))]
//
// Rustdoc features:
#![feature(doc_cfg)]

View File

@@ -28,7 +28,6 @@ use crate::fmt::{Debug, Display};
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "Error")]
#[rustc_has_incoherent_inherent_impls]
#[cfg_attr(not(bootstrap), allow(multiple_supertrait_upcastable))]
pub trait Error: Debug + Display {
/// The lower-level source of this error, if any.
///

View File

@@ -95,7 +95,6 @@
#![warn(missing_docs)]
#![allow(explicit_outlives_requirements)]
#![allow(incomplete_features)]
#![cfg_attr(not(bootstrap), warn(multiple_supertrait_upcastable))]
//
// Library features:
#![feature(const_align_offset)]
@@ -232,7 +231,6 @@
#![feature(unsized_fn_params)]
#![feature(asm_const)]
#![feature(const_transmute_copy)]
#![cfg_attr(not(bootstrap), feature(multiple_supertrait_upcastable))]
//
// Target features:
#![feature(arm_target_feature)]

View File

@@ -34,7 +34,7 @@ pub mod process;
pub mod stdio;
pub mod thread;
pub mod thread_local_key;
pub mod thread_parker;
pub mod thread_parking;
pub mod time;
mod condvar;

View File

@@ -65,9 +65,9 @@ mod task_queue {
/// execution. The signal is sent once all TLS destructors have finished at
/// which point no new thread locals should be created.
pub mod wait_notify {
use super::super::thread_parker::Parker;
use crate::pin::Pin;
use crate::sync::Arc;
use crate::sys_common::thread_parking::Parker;
pub struct Notifier(Arc<Parker>);
@@ -87,14 +87,14 @@ pub mod wait_notify {
/// called, this will return immediately, otherwise the current thread
/// is blocked until notified.
pub fn wait(self) {
// This is not actually `unsafe`, but it uses the `Parker` API,
// which needs `unsafe` on some platforms.
// SAFETY:
// This is only ever called on one thread.
unsafe { Pin::new(&*self.0).park() }
}
}
pub fn new() -> (Notifier, Waiter) {
let inner = Arc::new(Parker::new_internal());
let inner = Arc::new(Parker::new());
(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_local_dtor;
pub mod thread_local_key;
pub mod thread_parker;
pub mod thread_parking;
pub mod time;
#[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 {}
impl Parker {
pub unsafe fn new(parker: *mut Parker) {
pub unsafe fn new_in_place(parker: *mut Parker) {
let semaphore = dispatch_semaphore_create(0);
assert!(
!semaphore.is_null(),

View File

@@ -24,7 +24,7 @@ cfg_if::cfg_if! {
pub use darwin::Parker;
} else if #[cfg(target_os = "netbsd")] {
mod netbsd;
pub use netbsd::Parker;
pub use netbsd::{current, park, park_timeout, unpark, ThreadId};
} else {
mod pthread;
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
/// 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.
// This could lead to undefined behaviour when deadlocking. This is avoided
// 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_local_dtor;
pub mod thread_local_key;
pub mod thread_parker;
pub mod thread_parking;
pub mod time;
cfg_if::cfg_if! {
if #[cfg(not(target_vendor = "uwp"))] {

View File

@@ -97,7 +97,7 @@ const NOTIFIED: i8 = 1;
impl Parker {
/// Construct the Windows parker. The UNIX parker 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(Self { state: AtomicI8::new(EMPTY) });
}

View File

@@ -30,7 +30,7 @@ pub mod process;
pub mod thread;
pub mod thread_info;
pub mod thread_local_dtor;
pub mod thread_parker;
pub mod thread_parking;
pub mod wstr;
pub mod wtf8;

View File

@@ -35,7 +35,7 @@ pub struct Parker {
impl Parker {
/// Construct the futex parker. The UNIX parker 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(Self { state: AtomicU32::new(EMPTY) });
}

View File

@@ -19,7 +19,7 @@ pub struct Parker {
impl Parker {
/// Construct the generic parker. The UNIX parker 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: AtomicUsize::new(EMPTY),
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;
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")] {
mod wait_flag;
pub use wait_flag::Parker;
} else if #[cfg(any(windows, target_family = "unix"))] {
pub use crate::sys::thread_parker::Parker;
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
pub use crate::sys::thread_parker::Parker;
pub use crate::sys::thread_parking::Parker;
} else {
mod generic;
pub use generic::Parker;

View File

@@ -41,7 +41,7 @@ pub struct Parker {
impl Parker {
/// Construct a parker for the current thread. The UNIX parker
/// 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() })
}

View File

@@ -173,7 +173,7 @@ use crate::sync::Arc;
use crate::sys::thread as imp;
use crate::sys_common::thread;
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::time::Duration;
@@ -1216,7 +1216,7 @@ impl Thread {
let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
addr_of_mut!((*ptr).name).write(name);
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())
};

View File

@@ -97,18 +97,36 @@ impl RunConfig<'_> {
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>> {
let mut crates = Vec::new();
for krate in &self.paths {
let path = krate.assert_single_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)
}
}
/// 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 {
default: bool,
only_hosts: bool,

View File

@@ -99,19 +99,13 @@ impl Step for Std {
cargo_subcommand(builder.kind),
);
std_cargo(builder, target, compiler.stage, &mut cargo);
cargo.args(args(builder));
builder.info(&format!(
"Checking stage{} std artifacts ({} -> {})",
"Checking stage{} library artifacts ({} -> {})",
builder.top_stage, &compiler.host, target
));
run_cargo(
builder,
cargo,
args(builder),
&libstd_stamp(builder, compiler, target),
vec![],
true,
);
run_cargo(builder, cargo, &libstd_stamp(builder, compiler, target), vec![], true);
// We skip populating the sysroot in non-zero stage because that'll lead
// 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)) {
cargo.arg("-p").arg(krate.name);
}
cargo.args(args(builder));
builder.info(&format!(
"Checking stage{} std test/bench/example targets ({} -> {})",
"Checking stage{} library test/bench/example targets ({} -> {})",
builder.top_stage, &compiler.host, target
));
run_cargo(
builder,
cargo,
args(builder),
&libstd_test_stamp(builder, compiler, target),
vec![],
true,
);
run_cargo(builder, cargo, &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)) {
cargo.arg("-p").arg(krate.name);
}
cargo.args(args(builder));
builder.info(&format!(
"Checking stage{} compiler artifacts ({} -> {})",
builder.top_stage, &compiler.host, target
));
run_cargo(
builder,
cargo,
args(builder),
&librustc_stamp(builder, compiler, target),
vec![],
true,
);
run_cargo(builder, cargo, &librustc_stamp(builder, compiler, target), vec![], true);
let libdir = builder.sysroot_libdir(compiler, target);
let hostdir = builder.sysroot_libdir(compiler, compiler.host);
@@ -290,6 +272,7 @@ impl Step for CodegenBackend {
.arg("--manifest-path")
.arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend)));
rustc_cargo_env(builder, &mut cargo, target);
cargo.args(args(builder));
builder.info(&format!(
"Checking stage{} {} artifacts ({} -> {})",
@@ -299,7 +282,6 @@ impl Step for CodegenBackend {
run_cargo(
builder,
cargo,
args(builder),
&codegen_backend_stamp(builder, compiler, target, backend),
vec![],
true,
@@ -355,11 +337,13 @@ impl Step for RustAnalyzer {
cargo.arg("--benches");
}
cargo.args(args(builder));
builder.info(&format!(
"Checking stage{} {} artifacts ({} -> {})",
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
/// compiler for the specified target.
@@ -413,6 +397,8 @@ macro_rules! tool_check_step {
cargo.arg("--all-targets");
}
cargo.args(args(builder));
// Enable internal lints for clippy and rustdoc
// 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
@@ -428,7 +414,6 @@ macro_rules! tool_check_step {
run_cargo(
builder,
cargo,
args(builder),
&stamp(builder, compiler, target),
vec![],
true,

View File

@@ -9,11 +9,10 @@ use std::fs;
use std::io::{self, ErrorKind};
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::config::TargetSelection;
use crate::util::t;
use crate::{Build, Mode, Subcommand};
use crate::{Build, Compiler, Mode, Subcommand};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CleanAll {}
@@ -40,7 +39,7 @@ macro_rules! clean_crate_tree {
( $( $name:ident, $mode:path, $root_crate:literal);+ $(;)? ) => { $(
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct $name {
target: TargetSelection,
compiler: Compiler,
crates: Interned<Vec<String>>,
}
@@ -54,22 +53,21 @@ macro_rules! clean_crate_tree {
fn make_run(run: RunConfig<'_>) {
let builder = run.builder;
if builder.top_stage != 0 {
panic!("non-stage-0 clean not supported for individual crates");
}
builder.ensure(Self { crates: run.cargo_crates_in_set(), target: run.target });
let compiler = builder.compiler(builder.top_stage, run.target);
builder.ensure(Self { crates: run.cargo_crates_in_set(), compiler });
}
fn run(self, builder: &Builder<'_>) -> Self::Output {
let compiler = builder.compiler(0, self.target);
let mut cargo = builder.bare_cargo(compiler, $mode, self.target, "clean");
let compiler = self.compiler;
let target = compiler.host;
let mut cargo = builder.bare_cargo(compiler, $mode, target, "clean");
for krate in &*self.crates {
cargo.arg(krate);
}
builder.info(&format!(
"Cleaning stage{} {} artifacts ({} -> {})",
compiler.stage, stringify!($name).to_lowercase(), &compiler.host, self.target
"Cleaning{} stage{} {} artifacts ({} -> {})",
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,

View File

@@ -18,6 +18,7 @@ use std::str;
use serde::Deserialize;
use crate::builder::crate_description;
use crate::builder::Cargo;
use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
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);
if compiler_to_use != compiler {
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
// 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");
std_cargo(builder, target, compiler.stage, &mut cargo);
for krate in &*self.crates {
cargo.arg("-p").arg(krate);
}
builder.info(&format!(
"Building stage{} std artifacts ({} -> {})",
compiler.stage, &compiler.host, target
"Building{} stage{} library artifacts ({} -> {})",
crate_description(&self.crates),
compiler.stage,
&compiler.host,
target,
));
run_cargo(
builder,
cargo,
self.crates.to_vec(),
&libstd_stamp(builder, compiler, target),
target_deps,
false,
);
run_cargo(builder, cargo, &libstd_stamp(builder, compiler, target), target_deps, false);
builder.ensure(StdLink::from_std(
self,
@@ -425,7 +428,7 @@ impl Step for StdLink {
let target_compiler = self.target_compiler;
let target = self.target;
builder.info(&format!(
"Copying stage{} std from stage{} ({} -> {} / {})",
"Copying stage{} library from stage{} ({} -> {} / {})",
target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, 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!(
"Building stage{} compiler artifacts ({} -> {})",
compiler.stage, &compiler.host, target
"Building{} stage{} compiler artifacts ({} -> {})",
crate_description(&self.crates),
compiler.stage,
&compiler.host,
target,
));
run_cargo(
builder,
cargo,
self.crates.to_vec(),
&librustc_stamp(builder, compiler, target),
vec![],
false,
);
run_cargo(builder, cargo, &librustc_stamp(builder, compiler, target), vec![], false);
builder.ensure(RustcLink::from_rustc(
self,
@@ -981,7 +984,7 @@ impl Step for CodegenBackend {
"Building stage{} codegen backend {} ({} -> {})",
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() {
return;
}
@@ -1405,7 +1408,6 @@ pub fn add_to_sysroot(
pub fn run_cargo(
builder: &Builder<'_>,
cargo: Cargo,
tail_args: Vec<String>,
stamp: &Path,
additional_target_deps: Vec<(PathBuf, DependencyType)>,
is_check: bool,
@@ -1431,7 +1433,7 @@ pub fn run_cargo(
// files we need to probe for later.
let mut deps = 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 {
CargoMessage::CompilerArtifact {
filenames,
@@ -1546,7 +1548,6 @@ pub fn run_cargo(
pub fn stream_cargo(
builder: &Builder<'_>,
cargo: Cargo,
tail_args: Vec<String>,
cb: &mut dyn FnMut(CargoMessage<'_>),
) -> bool {
let mut cargo = Command::from(cargo);
@@ -1566,10 +1567,6 @@ pub fn stream_cargo(
}
cargo.arg("--message-format").arg(message_format).stdout(Stdio::piped());
for arg in tail_args {
cargo.arg(arg);
}
builder.verbose(&format!("running: {:?}", cargo));
let mut child = match cargo.spawn() {
Ok(child) => child,

View File

@@ -12,6 +12,7 @@ use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use crate::builder::crate_description;
use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
use crate::cache::{Interned, INTERNER};
use crate::compile;
@@ -558,7 +559,8 @@ fn doc_std(
requested_crates: &[String],
) {
builder.info(&format!(
"Documenting stage{} std ({}) in {} format",
"Documenting{} stage{} library ({}) in {} format",
crate_description(requested_crates),
stage,
target,
format.as_str()

View File

@@ -352,6 +352,8 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
// fn usage()
let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! {
println!("{}", opts.usage(subcommand_help));
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.
@@ -366,19 +368,17 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
let build = Build::new(config);
let paths = Builder::get_help(&build, subcommand);
println!("{}", opts.usage(subcommand_help));
if let Some(s) = paths {
if verbose {
println!("{}", s);
} else {
panic!("No paths available for subcommand `{}`", subcommand.as_str());
}
} else {
println!(
"Run `./x.py {} -h -v` to see a list of available paths.",
subcommand.as_str()
);
}
} else if verbose {
panic!("No paths available for subcommand `{}`", subcommand.as_str());
}
crate::detail_exit(exit_code);
};

View File

@@ -79,20 +79,15 @@ fn update_rustfmt_version(build: &Builder<'_>) {
///
/// Returns `None` if all files should be formatted.
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) {
return None;
}
let merge_base =
output(build.config.git().arg("merge-base").arg(&format!("{remote}/master")).arg("HEAD"));
Some(
output(
build
.config
.git()
.arg("diff-index")
.arg("--name-only")
.arg("--merge-base")
.arg(&format!("{remote}/master")),
)
output(build.config.git().arg("diff-index").arg("--name-only").arg(merge_base.trim()))
.lines()
.map(|s| s.trim().to_owned())
.filter(|f| Path::new(f).extension().map_or(false, |ext| ext == "rs"))

View File

@@ -11,6 +11,7 @@ use std::iter;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use crate::builder::crate_description;
use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
use crate::cache::Interned;
use crate::compile;
@@ -2154,8 +2155,12 @@ impl Step for Crate {
}
builder.info(&format!(
"{} {:?} stage{} ({} -> {})",
test_kind, self.crates, compiler.stage, &compiler.host, target
"{}{} stage{} ({} -> {})",
test_kind,
crate_description(&self.crates),
compiler.stage,
&compiler.host,
target
));
let _time = util::timeit(&builder);
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));
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
match tool {
"rls" | "cargo" | "clippy-driver" | "miri" | "rustfmt" => {}

View File

@@ -131,6 +131,13 @@ pub struct 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(_: ()) { }
// CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]]
pub fn foo1(_: c_void, _: ()) { }
@@ -425,6 +432,12 @@ pub fn foo145(_: Type13, _: Type13) { }
// CHECK: define{{.*}}foo145{{.*}}!type ![[TYPE145:[0-9]+]]
pub fn foo146(_: Type13, _: Type13, _: Type13) { }
// 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: ![[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: ![[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: ![[TYPE144]] = !{i64 0, !"_ZTSFvu3refIu3refIvEEE"}
// CHECK: ![[TYPE145]] = !{i64 0, !"_ZTSFvu3refIu3refIvEES0_E"}
// CHECK: ![[TYPE146]] = !{i64 0, !"_ZTSFvu3refIu3refIvEES0_S0_E"}
// CHECK: ![[TYPE144]] = !{i64 0, !"_ZTSFvu3refIvEE"}
// CHECK: ![[TYPE145]] = !{i64 0, !"_ZTSFvu3refIvES_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",
{"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"
walkdir = "2"
ignore = "0.4.18"
termcolor = "1.1.3"
[[bin]]
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
}
macro_rules! some_or_continue {
($e:expr) => {
match $e {
Some(e) => e,
None => continue,
}
};
}
fn extract_error_codes(
f: &str,
error_codes: &mut HashMap<String, ErrorCodeStatus>,
@@ -122,10 +113,16 @@ fn extract_error_codes(
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)
.canonicalize()
.expect("failed to canonicalize error explanation file path");
match read_to_string(&path) {
Ok(content) => {
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"]
let regex = Regex::new(r#"[(,"\s](E\d{4})[,)"]"#).unwrap();
println!("Checking which error codes lack tests...");
for path in paths {
walk(path, &mut filter_dirs, &mut |entry, contents| {
let file_name = entry.file_name();
@@ -245,20 +240,15 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
});
}
if found_explanations == 0 {
eprintln!("No error code explanation was tested!");
*bad = true;
tidy_error!(bad, "No error code explanation was tested!");
}
if found_tests == 0 {
eprintln!("No error code was found in compilation errors!");
*bad = true;
tidy_error!(bad, "No error code was found in compilation errors!");
}
if explanations.is_empty() {
eprintln!("No error code explanation was found!");
*bad = true;
tidy_error!(bad, "No error code explanation was found!");
}
if errors.is_empty() {
println!("Found {} error codes", error_codes.len());
for (err_code, error_status) in &error_codes {
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!"));
@@ -310,11 +300,6 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
}
errors.sort();
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
//! 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:
///
/// * The expression that failed
@@ -26,18 +30,27 @@ macro_rules! t {
}
macro_rules! tidy_error {
($bad:expr, $fmt:expr) => ({
*$bad = true;
eprint!("tidy error: ");
eprintln!($fmt);
});
($bad:expr, $fmt:expr, $($arg:tt)*) => ({
*$bad = true;
eprint!("tidy error: ");
eprintln!($fmt, $($arg)*);
($bad:expr, $($fmt:tt)*) => ({
$crate::tidy_error($bad, format_args!($($fmt)*)).expect("failed to output error");
});
}
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 bins;
pub mod debug_artifacts;