Auto merge of #106324 - compiler-errors:rollup-2m9njin, r=compiler-errors
Rollup of 9 pull requests Successful merges: - #105903 (Unify id-based thread parking implementations) - #106232 (CFI: Monomorphize transparent ADTs before typeid) - #106248 (Revert "Implement allow-by-default `multiple_supertrait_upcastable` lint") - #106286 (Make tidy errors red) - #106295 (Extend scraped examples layout GUI test for position of buttons) - #106305 ( bootstrap: Get rid of tail_args in stream_cargo) - #106310 (Dont use `--merge-base` during bootstrap formatting subcommand) - #106314 (Fix panic on `x build --help`) - #106317 (Only deduplicate stack traces for good path bugs) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
@@ -2675,9 +2675,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
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",
|
||||
]
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
use crate::{LateContext, LateLintPass, LintContext};
|
||||
|
||||
use rustc_errors::DelayDm;
|
||||
use rustc_hir as hir;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_lint! {
|
||||
/// The `multiple_supertrait_upcastable` lint detects when an object-safe trait has multiple
|
||||
/// supertraits.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// trait A {}
|
||||
/// trait B {}
|
||||
///
|
||||
/// #[warn(multiple_supertrait_upcastable)]
|
||||
/// trait C: A + B {}
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// To support upcasting with multiple supertraits, we need to store multiple vtables and this
|
||||
/// can result in extra space overhead, even if no code actually uses upcasting.
|
||||
/// This lint allows users to identify when such scenarios occur and to decide whether the
|
||||
/// additional overhead is justified.
|
||||
pub MULTIPLE_SUPERTRAIT_UPCASTABLE,
|
||||
Allow,
|
||||
"detect when an object-safe trait has multiple supertraits",
|
||||
@feature_gate = sym::multiple_supertrait_upcastable;
|
||||
}
|
||||
|
||||
declare_lint_pass!(MultipleSupertraitUpcastable => [MULTIPLE_SUPERTRAIT_UPCASTABLE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
|
||||
let def_id = item.owner_id.to_def_id();
|
||||
if let hir::ItemKind::Trait(_, _, _, _, _) = item.kind
|
||||
&& cx.tcx.is_object_safe(def_id)
|
||||
{
|
||||
let direct_super_traits_iter = cx.tcx
|
||||
.super_predicates_of(def_id)
|
||||
.predicates
|
||||
.into_iter()
|
||||
.filter_map(|(pred, _)| pred.to_opt_poly_trait_pred());
|
||||
if direct_super_traits_iter.count() > 1 {
|
||||
cx.struct_span_lint(
|
||||
MULTIPLE_SUPERTRAIT_UPCASTABLE,
|
||||
cx.tcx.def_span(def_id),
|
||||
DelayDm(|| {
|
||||
format!(
|
||||
"`{}` is object-safe and has multiple supertraits",
|
||||
item.ident,
|
||||
)
|
||||
}),
|
||||
|diag| diag,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -944,7 +944,6 @@ symbols! {
|
||||
mul,
|
||||
mul_assign,
|
||||
mul_with_overflow,
|
||||
multiple_supertrait_upcastable,
|
||||
must_not_suspend,
|
||||
must_use,
|
||||
naked,
|
||||
|
||||
@@ -164,6 +164,7 @@ fn encode_const<'tcx>(
|
||||
|
||||
/// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for
|
||||
/// 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>>,
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
//! Thread parking based on SGX events.
|
||||
|
||||
use super::abi::{thread, usercalls};
|
||||
use crate::io::ErrorKind;
|
||||
use crate::pin::Pin;
|
||||
use crate::ptr::{self, NonNull};
|
||||
use crate::sync::atomic::AtomicPtr;
|
||||
use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
|
||||
use crate::time::Duration;
|
||||
use fortanix_sgx_abi::{EV_UNPARK, WAIT_INDEFINITE};
|
||||
|
||||
// The TCS structure must be page-aligned (this is checked by EENTER), so these cannot
|
||||
// be valid pointers
|
||||
const EMPTY: *mut u8 = ptr::invalid_mut(1);
|
||||
const NOTIFIED: *mut u8 = ptr::invalid_mut(2);
|
||||
|
||||
pub struct Parker {
|
||||
/// The park state. One of EMPTY, NOTIFIED or a TCS address.
|
||||
/// A state change to NOTIFIED must be done with release ordering
|
||||
/// and be observed with acquire ordering so that operations after
|
||||
/// `thread::park` returns will not occur before the unpark message
|
||||
/// was sent.
|
||||
state: AtomicPtr<u8>,
|
||||
}
|
||||
|
||||
impl Parker {
|
||||
/// Construct the thread parker. The UNIX parker implementation
|
||||
/// requires this to happen in-place.
|
||||
pub unsafe fn new(parker: *mut Parker) {
|
||||
unsafe { parker.write(Parker::new_internal()) }
|
||||
}
|
||||
|
||||
pub(super) fn new_internal() -> Parker {
|
||||
Parker { state: AtomicPtr::new(EMPTY) }
|
||||
}
|
||||
|
||||
// This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
|
||||
pub unsafe fn park(self: Pin<&Self>) {
|
||||
if self.state.load(Acquire) != NOTIFIED {
|
||||
let mut prev = EMPTY;
|
||||
loop {
|
||||
// Guard against changing TCS addresses by always setting the state to
|
||||
// the current value.
|
||||
let tcs = thread::current().as_ptr();
|
||||
if self.state.compare_exchange(prev, tcs, Relaxed, Acquire).is_ok() {
|
||||
let event = usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap();
|
||||
assert!(event & EV_UNPARK == EV_UNPARK);
|
||||
prev = tcs;
|
||||
} else {
|
||||
// The state was definitely changed by another thread at this point.
|
||||
// The only time this occurs is when the state is changed to NOTIFIED.
|
||||
// We observed this change with acquire ordering, so we can simply
|
||||
// change the state to EMPTY with a relaxed store.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, the token was definately read with acquire ordering,
|
||||
// so this can be a relaxed store.
|
||||
self.state.store(EMPTY, Relaxed);
|
||||
}
|
||||
|
||||
// This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
|
||||
pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
|
||||
let timeout = u128::min(dur.as_nanos(), WAIT_INDEFINITE as u128 - 1) as u64;
|
||||
let tcs = thread::current().as_ptr();
|
||||
|
||||
if self.state.load(Acquire) != NOTIFIED {
|
||||
if self.state.compare_exchange(EMPTY, tcs, Relaxed, Acquire).is_ok() {
|
||||
match usercalls::wait(EV_UNPARK, timeout) {
|
||||
Ok(event) => assert!(event & EV_UNPARK == EV_UNPARK),
|
||||
Err(e) => {
|
||||
assert!(matches!(e.kind(), ErrorKind::TimedOut | ErrorKind::WouldBlock))
|
||||
}
|
||||
}
|
||||
|
||||
// Swap to provide acquire ordering even if the timeout occurred
|
||||
// before the token was set. This situation can result in spurious
|
||||
// wakeups on the next call to `park_timeout`, but it is better to let
|
||||
// those be handled by the user than do some perhaps unnecessary, but
|
||||
// always expensive guarding.
|
||||
self.state.swap(EMPTY, Acquire);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The token was already read with `acquire` ordering, this can be a store.
|
||||
self.state.store(EMPTY, Relaxed);
|
||||
}
|
||||
|
||||
// This implementation doesn't require `Pin`, but other implementations do.
|
||||
pub fn unpark(self: Pin<&Self>) {
|
||||
let state = self.state.swap(NOTIFIED, Release);
|
||||
|
||||
if !matches!(state, EMPTY | NOTIFIED) {
|
||||
// There is a thread waiting, wake it up.
|
||||
let tcs = NonNull::new(state).unwrap();
|
||||
// This will fail if the thread has already terminated or its TCS is destroyed
|
||||
// by the time the signal is sent, but that is fine. If another thread receives
|
||||
// the same TCS, it will receive this notification as a spurious wakeup, but
|
||||
// all users of `wait` should and (internally) do guard against those where
|
||||
// necessary.
|
||||
let _ = usercalls::send(EV_UNPARK, Some(tcs));
|
||||
}
|
||||
}
|
||||
}
|
||||
23
library/std/src/sys/sgx/thread_parking.rs
Normal file
23
library/std/src/sys/sgx/thread_parking.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use super::abi::usercalls;
|
||||
use crate::io::ErrorKind;
|
||||
use crate::time::Duration;
|
||||
use fortanix_sgx_abi::{EV_UNPARK, WAIT_INDEFINITE};
|
||||
|
||||
pub type ThreadId = fortanix_sgx_abi::Tcs;
|
||||
|
||||
pub use super::abi::thread::current;
|
||||
|
||||
pub fn park(_hint: usize) {
|
||||
usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap();
|
||||
}
|
||||
|
||||
pub fn park_timeout(dur: Duration, _hint: usize) {
|
||||
let timeout = u128::min(dur.as_nanos(), WAIT_INDEFINITE as u128 - 1) as u64;
|
||||
if let Err(e) = usercalls::wait(EV_UNPARK, timeout) {
|
||||
assert!(matches!(e.kind(), ErrorKind::TimedOut | ErrorKind::WouldBlock))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unpark(tid: ThreadId, _hint: usize) {
|
||||
let _ = usercalls::send(EV_UNPARK, Some(tid));
|
||||
}
|
||||
@@ -40,7 +40,7 @@ pub mod stdio;
|
||||
pub mod thread;
|
||||
pub mod thread_local_dtor;
|
||||
pub mod thread_local_key;
|
||||
pub mod thread_parker;
|
||||
pub mod thread_parking;
|
||||
pub mod time;
|
||||
|
||||
#[cfg(target_os = "espidf")]
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
use crate::ffi::{c_int, c_void};
|
||||
use crate::pin::Pin;
|
||||
use crate::ptr::{null, null_mut};
|
||||
use crate::sync::atomic::{
|
||||
AtomicU64,
|
||||
Ordering::{Acquire, Relaxed, Release},
|
||||
};
|
||||
use crate::time::Duration;
|
||||
use libc::{_lwp_self, clockid_t, lwpid_t, time_t, timespec, CLOCK_MONOTONIC};
|
||||
|
||||
extern "C" {
|
||||
fn ___lwp_park60(
|
||||
clock_id: clockid_t,
|
||||
flags: c_int,
|
||||
ts: *mut timespec,
|
||||
unpark: lwpid_t,
|
||||
hint: *const c_void,
|
||||
unparkhint: *const c_void,
|
||||
) -> c_int;
|
||||
fn _lwp_unpark(lwp: lwpid_t, hint: *const c_void) -> c_int;
|
||||
}
|
||||
|
||||
/// The thread is not parked and the token is not available.
|
||||
///
|
||||
/// Zero cannot be a valid LWP id, since it is used as empty value for the unpark
|
||||
/// argument in _lwp_park.
|
||||
const EMPTY: u64 = 0;
|
||||
/// The token is available. Do not park anymore.
|
||||
const NOTIFIED: u64 = u64::MAX;
|
||||
|
||||
pub struct Parker {
|
||||
/// The parker state. Contains either one of the two state values above or the LWP
|
||||
/// id of the parked thread.
|
||||
state: AtomicU64,
|
||||
}
|
||||
|
||||
impl Parker {
|
||||
pub unsafe fn new(parker: *mut Parker) {
|
||||
parker.write(Parker { state: AtomicU64::new(EMPTY) })
|
||||
}
|
||||
|
||||
// Does not actually need `unsafe` or `Pin`, but the pthread implementation does.
|
||||
pub unsafe fn park(self: Pin<&Self>) {
|
||||
// If the token has already been made available, we can skip
|
||||
// a bit of work, so check for it here.
|
||||
if self.state.load(Acquire) != NOTIFIED {
|
||||
let parked = _lwp_self() as u64;
|
||||
let hint = self.state.as_mut_ptr().cast();
|
||||
if self.state.compare_exchange(EMPTY, parked, Relaxed, Acquire).is_ok() {
|
||||
// Loop to guard against spurious wakeups.
|
||||
loop {
|
||||
___lwp_park60(0, 0, null_mut(), 0, hint, null());
|
||||
if self.state.load(Acquire) == NOTIFIED {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, the change to NOTIFIED has always been observed with acquire
|
||||
// ordering, so we can just use a relaxed store here (instead of a swap).
|
||||
self.state.store(EMPTY, Relaxed);
|
||||
}
|
||||
|
||||
// Does not actually need `unsafe` or `Pin`, but the pthread implementation does.
|
||||
pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
|
||||
if self.state.load(Acquire) != NOTIFIED {
|
||||
let parked = _lwp_self() as u64;
|
||||
let hint = self.state.as_mut_ptr().cast();
|
||||
let mut timeout = timespec {
|
||||
// Saturate so that the operation will definitely time out
|
||||
// (even if it is after the heat death of the universe).
|
||||
tv_sec: dur.as_secs().try_into().ok().unwrap_or(time_t::MAX),
|
||||
tv_nsec: dur.subsec_nanos().into(),
|
||||
};
|
||||
|
||||
if self.state.compare_exchange(EMPTY, parked, Relaxed, Acquire).is_ok() {
|
||||
// Timeout needs to be mutable since it is modified on NetBSD 9.0 and
|
||||
// above.
|
||||
___lwp_park60(CLOCK_MONOTONIC, 0, &mut timeout, 0, hint, null());
|
||||
// Use a swap to get acquire ordering even if the token was set after
|
||||
// the timeout occurred.
|
||||
self.state.swap(EMPTY, Acquire);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.state.store(EMPTY, Relaxed);
|
||||
}
|
||||
|
||||
// Does not actually need `Pin`, but the pthread implementation does.
|
||||
pub fn unpark(self: Pin<&Self>) {
|
||||
let state = self.state.swap(NOTIFIED, Release);
|
||||
if !matches!(state, EMPTY | NOTIFIED) {
|
||||
let lwp = state as lwpid_t;
|
||||
let hint = self.state.as_mut_ptr().cast();
|
||||
|
||||
// If the parking thread terminated and did not actually park, this will
|
||||
// probably return an error, which is OK. In the worst case, another
|
||||
// thread has received the same LWP id. It will then receive a spurious
|
||||
// wakeup, but those are allowable per the API contract. The same reasoning
|
||||
// applies if a timeout occurred before this call, but the state was not
|
||||
// yet reset.
|
||||
|
||||
// SAFETY:
|
||||
// The syscall has no invariants to hold. Only unsafe because it is an
|
||||
// extern function.
|
||||
unsafe {
|
||||
_lwp_unpark(lwp, hint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ unsafe impl Sync for Parker {}
|
||||
unsafe impl Send for Parker {}
|
||||
|
||||
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(),
|
||||
@@ -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;
|
||||
52
library/std/src/sys/unix/thread_parking/netbsd.rs
Normal file
52
library/std/src/sys/unix/thread_parking/netbsd.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use crate::ffi::{c_int, c_void};
|
||||
use crate::ptr;
|
||||
use crate::time::Duration;
|
||||
use libc::{_lwp_self, clockid_t, lwpid_t, time_t, timespec, CLOCK_MONOTONIC};
|
||||
|
||||
extern "C" {
|
||||
fn ___lwp_park60(
|
||||
clock_id: clockid_t,
|
||||
flags: c_int,
|
||||
ts: *mut timespec,
|
||||
unpark: lwpid_t,
|
||||
hint: *const c_void,
|
||||
unparkhint: *const c_void,
|
||||
) -> c_int;
|
||||
fn _lwp_unpark(lwp: lwpid_t, hint: *const c_void) -> c_int;
|
||||
}
|
||||
|
||||
pub type ThreadId = lwpid_t;
|
||||
|
||||
#[inline]
|
||||
pub fn current() -> ThreadId {
|
||||
unsafe { _lwp_self() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn park(hint: usize) {
|
||||
unsafe {
|
||||
___lwp_park60(0, 0, ptr::null_mut(), 0, ptr::invalid(hint), ptr::null());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn park_timeout(dur: Duration, hint: usize) {
|
||||
let mut timeout = timespec {
|
||||
// Saturate so that the operation will definitely time out
|
||||
// (even if it is after the heat death of the universe).
|
||||
tv_sec: dur.as_secs().try_into().ok().unwrap_or(time_t::MAX),
|
||||
tv_nsec: dur.subsec_nanos().into(),
|
||||
};
|
||||
|
||||
// Timeout needs to be mutable since it is modified on NetBSD 9.0 and
|
||||
// above.
|
||||
unsafe {
|
||||
___lwp_park60(CLOCK_MONOTONIC, 0, &mut timeout, 0, ptr::invalid(hint), ptr::null());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn unpark(tid: ThreadId, hint: usize) {
|
||||
unsafe {
|
||||
_lwp_unpark(tid, ptr::invalid(hint));
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ impl Parker {
|
||||
///
|
||||
/// # Safety
|
||||
/// 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
|
||||
@@ -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"))] {
|
||||
|
||||
@@ -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) });
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) });
|
||||
}
|
||||
|
||||
@@ -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(()),
|
||||
108
library/std/src/sys_common/thread_parking/id.rs
Normal file
108
library/std/src/sys_common/thread_parking/id.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
//! Thread parking using thread ids.
|
||||
//!
|
||||
//! Some platforms (notably NetBSD) have thread parking primitives whose semantics
|
||||
//! match those offered by `thread::park`, with the difference that the thread to
|
||||
//! be unparked is referenced by a platform-specific thread id. Since the thread
|
||||
//! parker is constructed before that id is known, an atomic state variable is used
|
||||
//! to manage the park state and propagate the thread id. This also avoids platform
|
||||
//! calls in the case where `unpark` is called before `park`.
|
||||
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::pin::Pin;
|
||||
use crate::sync::atomic::{
|
||||
fence, AtomicI8,
|
||||
Ordering::{Acquire, Relaxed, Release},
|
||||
};
|
||||
use crate::sys::thread_parking::{current, park, park_timeout, unpark, ThreadId};
|
||||
use crate::time::Duration;
|
||||
|
||||
pub struct Parker {
|
||||
state: AtomicI8,
|
||||
tid: UnsafeCell<Option<ThreadId>>,
|
||||
}
|
||||
|
||||
const PARKED: i8 = -1;
|
||||
const EMPTY: i8 = 0;
|
||||
const NOTIFIED: i8 = 1;
|
||||
|
||||
impl Parker {
|
||||
pub fn new() -> Parker {
|
||||
Parker { state: AtomicI8::new(EMPTY), tid: UnsafeCell::new(None) }
|
||||
}
|
||||
|
||||
/// Create a new thread parker. UNIX requires this to happen in-place.
|
||||
pub unsafe fn new_in_place(parker: *mut Parker) {
|
||||
parker.write(Parker::new())
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// * must always be called from the same thread
|
||||
/// * must be called before the state is set to PARKED
|
||||
unsafe fn init_tid(&self) {
|
||||
// The field is only ever written to from this thread, so we don't need
|
||||
// synchronization to read it here.
|
||||
if self.tid.get().read().is_none() {
|
||||
// Because this point is only reached once, before the state is set
|
||||
// to PARKED for the first time, the non-atomic write here can not
|
||||
// conflict with reads by other threads.
|
||||
self.tid.get().write(Some(current()));
|
||||
// Ensure that the write can be observed by all threads reading the
|
||||
// state. Synchronizes with the acquire barrier in `unpark`.
|
||||
fence(Release);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn park(self: Pin<&Self>) {
|
||||
self.init_tid();
|
||||
|
||||
// Changes NOTIFIED to EMPTY and EMPTY to PARKED.
|
||||
let mut state = self.state.fetch_sub(1, Acquire).wrapping_sub(1);
|
||||
if state == PARKED {
|
||||
// Loop to guard against spurious wakeups.
|
||||
while state == PARKED {
|
||||
park(self.state.as_mut_ptr().addr());
|
||||
state = self.state.load(Acquire);
|
||||
}
|
||||
|
||||
// Since the state change has already been observed with acquire
|
||||
// ordering, the state can be reset with a relaxed store instead
|
||||
// of a swap.
|
||||
self.state.store(EMPTY, Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
|
||||
self.init_tid();
|
||||
|
||||
let state = self.state.fetch_sub(1, Acquire).wrapping_sub(1);
|
||||
if state == PARKED {
|
||||
park_timeout(dur, self.state.as_mut_ptr().addr());
|
||||
// Swap to ensure that we observe all state changes with acquire
|
||||
// ordering, even if the state has been changed after the timeout
|
||||
// occured.
|
||||
self.state.swap(EMPTY, Acquire);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unpark(self: Pin<&Self>) {
|
||||
let state = self.state.swap(NOTIFIED, Release);
|
||||
if state == PARKED {
|
||||
// Synchronize with the release fence in `init_tid` to observe the
|
||||
// write to `tid`.
|
||||
fence(Acquire);
|
||||
// # Safety
|
||||
// The thread id is initialized before the state is set to `PARKED`
|
||||
// for the first time and is not written to from that point on
|
||||
// (negating the need for an atomic read).
|
||||
let tid = unsafe { self.tid.get().read().unwrap_unchecked() };
|
||||
// It is possible that the waiting thread woke up because of a timeout
|
||||
// and terminated before this call is made. This call then returns an
|
||||
// error or wakes up an unrelated thread. The platform API and
|
||||
// environment does allow this, however.
|
||||
unpark(tid, self.state.as_mut_ptr().addr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Parker {}
|
||||
unsafe impl Sync for Parker {}
|
||||
@@ -11,13 +11,17 @@ cfg_if::cfg_if! {
|
||||
))] {
|
||||
mod futex;
|
||||
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;
|
||||
@@ -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() })
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -83,16 +83,11 @@ fn get_modified_rs_files(build: &Builder<'_>) -> Option<Vec<String>> {
|
||||
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"))
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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" => {}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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|})
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// check-pass
|
||||
|
||||
#![deny(multiple_supertrait_upcastable)]
|
||||
//~^ WARNING unknown lint: `multiple_supertrait_upcastable`
|
||||
//~| WARNING unknown lint: `multiple_supertrait_upcastable`
|
||||
//~| WARNING unknown lint: `multiple_supertrait_upcastable`
|
||||
#![warn(multiple_supertrait_upcastable)]
|
||||
//~^ WARNING unknown lint: `multiple_supertrait_upcastable`
|
||||
//~| WARNING unknown lint: `multiple_supertrait_upcastable`
|
||||
//~| WARNING unknown lint: `multiple_supertrait_upcastable`
|
||||
|
||||
fn main() {}
|
||||
@@ -1,57 +0,0 @@
|
||||
warning: unknown lint: `multiple_supertrait_upcastable`
|
||||
--> $DIR/feature-gate-multiple_supertrait_upcastable.rs:3:1
|
||||
|
|
||||
LL | #![deny(multiple_supertrait_upcastable)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the `multiple_supertrait_upcastable` lint is unstable
|
||||
= help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
|
||||
= note: `#[warn(unknown_lints)]` on by default
|
||||
|
||||
warning: unknown lint: `multiple_supertrait_upcastable`
|
||||
--> $DIR/feature-gate-multiple_supertrait_upcastable.rs:7:1
|
||||
|
|
||||
LL | #![warn(multiple_supertrait_upcastable)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the `multiple_supertrait_upcastable` lint is unstable
|
||||
= help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
|
||||
|
||||
warning: unknown lint: `multiple_supertrait_upcastable`
|
||||
--> $DIR/feature-gate-multiple_supertrait_upcastable.rs:3:1
|
||||
|
|
||||
LL | #![deny(multiple_supertrait_upcastable)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the `multiple_supertrait_upcastable` lint is unstable
|
||||
= help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
|
||||
|
||||
warning: unknown lint: `multiple_supertrait_upcastable`
|
||||
--> $DIR/feature-gate-multiple_supertrait_upcastable.rs:7:1
|
||||
|
|
||||
LL | #![warn(multiple_supertrait_upcastable)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the `multiple_supertrait_upcastable` lint is unstable
|
||||
= help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
|
||||
|
||||
warning: unknown lint: `multiple_supertrait_upcastable`
|
||||
--> $DIR/feature-gate-multiple_supertrait_upcastable.rs:3:1
|
||||
|
|
||||
LL | #![deny(multiple_supertrait_upcastable)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the `multiple_supertrait_upcastable` lint is unstable
|
||||
= help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
|
||||
|
||||
warning: unknown lint: `multiple_supertrait_upcastable`
|
||||
--> $DIR/feature-gate-multiple_supertrait_upcastable.rs:7:1
|
||||
|
|
||||
LL | #![warn(multiple_supertrait_upcastable)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the `multiple_supertrait_upcastable` lint is unstable
|
||||
= help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
|
||||
|
||||
warning: 6 warnings emitted
|
||||
|
||||
9
src/test/ui/object-safety/issue-106247.rs
Normal file
9
src/test/ui/object-safety/issue-106247.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
// check-pass
|
||||
|
||||
#![deny(where_clauses_object_safety)]
|
||||
|
||||
pub trait Trait {
|
||||
fn method(&self) where Self: Sync;
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -1,10 +0,0 @@
|
||||
#![feature(multiple_supertrait_upcastable)]
|
||||
#![deny(multiple_supertrait_upcastable)]
|
||||
|
||||
trait A {}
|
||||
trait B {}
|
||||
|
||||
trait C: A + B {}
|
||||
//~^ ERROR `C` is object-safe and has multiple supertraits
|
||||
|
||||
fn main() {}
|
||||
@@ -1,14 +0,0 @@
|
||||
error: `C` is object-safe and has multiple supertraits
|
||||
--> $DIR/multiple_supertrait_upcastable.rs:7:1
|
||||
|
|
||||
LL | trait C: A + B {}
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/multiple_supertrait_upcastable.rs:2:9
|
||||
|
|
||||
LL | #![deny(multiple_supertrait_upcastable)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
@@ -11,6 +11,7 @@ miropt-test-tools = { path = "../miropt-test-tools" }
|
||||
lazy_static = "1"
|
||||
walkdir = "2"
|
||||
ignore = "0.4.18"
|
||||
termcolor = "1.1.3"
|
||||
|
||||
[[bin]]
|
||||
name = "rust-tidy"
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user