Auto merge of #149979 - ChrisDenton:rollup-9rvyn3h, r=ChrisDenton

Rollup of 8 pull requests

Successful merges:

 - rust-lang/rust#148755 (Constify `DropGuard::dismiss` and trait impls)
 - rust-lang/rust#148825 (Add SystemTime::{MIN, MAX})
 - rust-lang/rust#149272 (Fix vec iter zst alignment)
 - rust-lang/rust#149417 (tidy: Detect outdated workspaces in workspace list)
 - rust-lang/rust#149773 (fix va_list test by adding a llvmir signext check)
 - rust-lang/rust#149894 (Update to mdbook 0.5)
 - rust-lang/rust#149955 (Fix typo in armv7a-vex-v5 documentation)
 - rust-lang/rust#149972 (Enable to ping LoongArch group via triagebot)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-12-14 12:32:36 +00:00
commit 0208ee09be
34 changed files with 691 additions and 893 deletions

View file

@ -411,7 +411,12 @@ impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> {
// SAFETY: same as for advance_by() // SAFETY: same as for advance_by()
self.end = unsafe { self.end.sub(step_size) }; self.end = unsafe { self.end.sub(step_size) };
} }
let to_drop = ptr::slice_from_raw_parts_mut(self.end as *mut T, step_size); let to_drop = if T::IS_ZST {
// ZST may cause unalignment
ptr::slice_from_raw_parts_mut(ptr::NonNull::<T>::dangling().as_ptr(), step_size)
} else {
ptr::slice_from_raw_parts_mut(self.end as *mut T, step_size)
};
// SAFETY: same as for advance_by() // SAFETY: same as for advance_by()
unsafe { unsafe {
ptr::drop_in_place(to_drop); ptr::drop_in_place(to_drop);

View file

@ -2717,3 +2717,35 @@ fn vec_null_ptr_roundtrip() {
let new = roundtripped.with_addr(ptr.addr()); let new = roundtripped.with_addr(ptr.addr());
unsafe { new.read() }; unsafe { new.read() };
} }
// Regression test for Undefined Behavior (UB) caused by IntoIter::nth_back (#148682)
// when dealing with high-aligned Zero-Sized Types (ZSTs).
use std::collections::{BTreeMap, BinaryHeap, HashMap, LinkedList, VecDeque};
#[test]
fn zst_collections_iter_nth_back_regression() {
#[repr(align(8))]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
struct Thing;
let v = vec![Thing, Thing];
let _ = v.into_iter().nth_back(1);
let mut d = VecDeque::new();
d.push_back(Thing);
d.push_back(Thing);
let _ = d.into_iter().nth_back(1);
let mut map = BTreeMap::new();
map.insert(0, Thing);
map.insert(1, Thing);
let _ = map.into_values().nth_back(0);
let mut hash_map = HashMap::new();
hash_map.insert(1, Thing);
hash_map.insert(2, Thing);
let _ = hash_map.into_values().nth(1);
let mut heap = BinaryHeap::new();
heap.push(Thing);
heap.push(Thing);
let _ = heap.into_iter().nth_back(1);
let mut list = LinkedList::new();
list.push_back(Thing);
list.push_back(Thing);
let _ = list.into_iter().nth_back(1);
}

View file

@ -1,4 +1,5 @@
use crate::fmt::{self, Debug}; use crate::fmt::{self, Debug};
use crate::marker::Destruct;
use crate::mem::ManuallyDrop; use crate::mem::ManuallyDrop;
use crate::ops::{Deref, DerefMut}; use crate::ops::{Deref, DerefMut};
@ -78,32 +79,37 @@ where
/// ///
/// let value = String::from("Nori likes chicken"); /// let value = String::from("Nori likes chicken");
/// let guard = DropGuard::new(value, |s| println!("{s}")); /// let guard = DropGuard::new(value, |s| println!("{s}"));
/// assert_eq!(guard.dismiss(), "Nori likes chicken"); /// assert_eq!(DropGuard::dismiss(guard), "Nori likes chicken");
/// ``` /// ```
#[unstable(feature = "drop_guard", issue = "144426")] #[unstable(feature = "drop_guard", issue = "144426")]
#[rustc_const_unstable(feature = "const_drop_guard", issue = "none")]
#[inline] #[inline]
pub fn dismiss(self) -> T { pub const fn dismiss(guard: Self) -> T
where
F: [const] Destruct,
{
// First we ensure that dropping the guard will not trigger // First we ensure that dropping the guard will not trigger
// its destructor // its destructor
let mut this = ManuallyDrop::new(self); let mut guard = ManuallyDrop::new(guard);
// Next we manually read the stored value from the guard. // Next we manually read the stored value from the guard.
// //
// SAFETY: this is safe because we've taken ownership of the guard. // SAFETY: this is safe because we've taken ownership of the guard.
let value = unsafe { ManuallyDrop::take(&mut this.inner) }; let value = unsafe { ManuallyDrop::take(&mut guard.inner) };
// Finally we drop the stored closure. We do this *after* having read // Finally we drop the stored closure. We do this *after* having read
// the value, so that even if the closure's `drop` function panics, // the value, so that even if the closure's `drop` function panics,
// unwinding still tries to drop the value. // unwinding still tries to drop the value.
// //
// SAFETY: this is safe because we've taken ownership of the guard. // SAFETY: this is safe because we've taken ownership of the guard.
unsafe { ManuallyDrop::drop(&mut this.f) }; unsafe { ManuallyDrop::drop(&mut guard.f) };
value value
} }
} }
#[unstable(feature = "drop_guard", issue = "144426")] #[unstable(feature = "drop_guard", issue = "144426")]
impl<T, F> Deref for DropGuard<T, F> #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl<T, F> const Deref for DropGuard<T, F>
where where
F: FnOnce(T), F: FnOnce(T),
{ {
@ -115,7 +121,8 @@ where
} }
#[unstable(feature = "drop_guard", issue = "144426")] #[unstable(feature = "drop_guard", issue = "144426")]
impl<T, F> DerefMut for DropGuard<T, F> #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl<T, F> const DerefMut for DropGuard<T, F>
where where
F: FnOnce(T), F: FnOnce(T),
{ {
@ -125,9 +132,10 @@ where
} }
#[unstable(feature = "drop_guard", issue = "144426")] #[unstable(feature = "drop_guard", issue = "144426")]
impl<T, F> Drop for DropGuard<T, F> #[rustc_const_unstable(feature = "const_drop_guard", issue = "none")]
impl<T, F> const Drop for DropGuard<T, F>
where where
F: FnOnce(T), F: [const] FnOnce(T),
{ {
fn drop(&mut self) { fn drop(&mut self) {
// SAFETY: `DropGuard` is in the process of being dropped. // SAFETY: `DropGuard` is in the process of being dropped.

View file

@ -815,7 +815,7 @@ fn drop_guard_into_inner() {
let dropped = Cell::new(false); let dropped = Cell::new(false);
let value = DropGuard::new(42, |_| dropped.set(true)); let value = DropGuard::new(42, |_| dropped.set(true));
let guard = DropGuard::new(value, |_| dropped.set(true)); let guard = DropGuard::new(value, |_| dropped.set(true));
let inner = guard.dismiss(); let inner = DropGuard::dismiss(guard);
assert_eq!(dropped.get(), false); assert_eq!(dropped.get(), false);
assert_eq!(*inner, 42); assert_eq!(*inner, 42);
} }
@ -837,7 +837,7 @@ fn drop_guard_always_drops_value_if_closure_drop_unwinds() {
// run the destructor of the value we passed, which we validate. // run the destructor of the value we passed, which we validate.
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let guard = DropGuard::new(value_with_tracked_destruction, closure_that_panics_on_drop); let guard = DropGuard::new(value_with_tracked_destruction, closure_that_panics_on_drop);
guard.dismiss(); DropGuard::dismiss(guard);
})); }));
assert!(value_was_dropped); assert!(value_was_dropped);
} }

View file

@ -15,6 +15,10 @@ struct Timespec {
} }
impl Timespec { impl Timespec {
const MAX: Timespec = Self::new(i64::MAX, 1_000_000_000 - 1);
const MIN: Timespec = Self::new(i64::MIN, 0);
const fn zero() -> Timespec { const fn zero() -> Timespec {
Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } }
} }
@ -209,6 +213,10 @@ pub struct SystemTime(Timespec);
pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero());
impl SystemTime { impl SystemTime {
pub const MAX: SystemTime = SystemTime { t: Timespec::MAX };
pub const MIN: SystemTime = SystemTime { t: Timespec::MIN };
pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime { pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime {
SystemTime(Timespec::new(tv_sec, tv_nsec)) SystemTime(Timespec::new(tv_sec, tv_nsec))
} }

View file

@ -28,6 +28,10 @@ impl Instant {
} }
impl SystemTime { impl SystemTime {
pub const MAX: SystemTime = SystemTime(Duration::MAX);
pub const MIN: SystemTime = SystemTime(Duration::ZERO);
pub fn now() -> SystemTime { pub fn now() -> SystemTime {
SystemTime(usercalls::insecure_time()) SystemTime(usercalls::insecure_time())
} }

View file

@ -10,6 +10,10 @@ pub struct SystemTime(abi::time_t);
pub const UNIX_EPOCH: SystemTime = SystemTime(0); pub const UNIX_EPOCH: SystemTime = SystemTime(0);
impl SystemTime { impl SystemTime {
pub const MAX: SystemTime = SystemTime(abi::time_t::MAX);
pub const MIN: SystemTime = SystemTime(abi::time_t::MIN);
pub fn now() -> SystemTime { pub fn now() -> SystemTime {
let rtc = unsafe { let rtc = unsafe {
let mut out = MaybeUninit::zeroed(); let mut out = MaybeUninit::zeroed();

View file

@ -70,6 +70,23 @@ impl Instant {
} }
impl SystemTime { impl SystemTime {
pub const MAX: SystemTime = MAX_UEFI_TIME;
pub const MIN: SystemTime = SystemTime::from_uefi(r_efi::efi::Time {
year: 1900,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0,
nanosecond: 0,
timezone: -1440,
daylight: 0,
pad1: 0,
pad2: 0,
})
.unwrap();
pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Option<Self> { pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Option<Self> {
match system_time_internal::from_uefi(&t) { match system_time_internal::from_uefi(&t) {
Some(x) => Some(Self(x)), Some(x) => Some(Self(x)),

View file

@ -30,6 +30,10 @@ pub(crate) struct Timespec {
} }
impl SystemTime { impl SystemTime {
pub const MAX: SystemTime = SystemTime { t: Timespec::MAX };
pub const MIN: SystemTime = SystemTime { t: Timespec::MIN };
#[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))] #[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))]
pub fn new(tv_sec: i64, tv_nsec: i64) -> Result<SystemTime, io::Error> { pub fn new(tv_sec: i64, tv_nsec: i64) -> Result<SystemTime, io::Error> {
Ok(SystemTime { t: Timespec::new(tv_sec, tv_nsec)? }) Ok(SystemTime { t: Timespec::new(tv_sec, tv_nsec)? })
@ -62,6 +66,13 @@ impl fmt::Debug for SystemTime {
} }
impl Timespec { impl Timespec {
const MAX: Timespec = unsafe { Self::new_unchecked(i64::MAX, 1_000_000_000 - 1) };
// As described below, on Apple OS, dates before epoch are represented differently.
// This is not an issue here however, because we are using tv_sec = i64::MIN,
// which will cause the compatibility wrapper to not be executed at all.
const MIN: Timespec = unsafe { Self::new_unchecked(i64::MIN, 0) };
const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec { const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec {
Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } } Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } }
} }

View file

@ -27,6 +27,10 @@ impl Instant {
} }
impl SystemTime { impl SystemTime {
pub const MAX: SystemTime = SystemTime(Duration::MAX);
pub const MIN: SystemTime = SystemTime(Duration::ZERO);
pub fn now() -> SystemTime { pub fn now() -> SystemTime {
panic!("time not implemented on this platform") panic!("time not implemented on this platform")
} }

View file

@ -64,6 +64,16 @@ impl Instant {
} }
impl SystemTime { impl SystemTime {
pub const MAX: SystemTime = SystemTime {
t: c::FILETIME {
dwLowDateTime: (i64::MAX & 0xFFFFFFFF) as u32,
dwHighDateTime: (i64::MAX >> 32) as u32,
},
};
pub const MIN: SystemTime =
SystemTime { t: c::FILETIME { dwLowDateTime: 0, dwHighDateTime: 0 } };
pub fn now() -> SystemTime { pub fn now() -> SystemTime {
unsafe { unsafe {
let mut t: SystemTime = mem::zeroed(); let mut t: SystemTime = mem::zeroed();
@ -101,8 +111,13 @@ impl SystemTime {
} }
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
let intervals = self.intervals().checked_sub(checked_dur2intervals(other)?)?; // Windows does not support times before 1601, hence why we don't
Some(SystemTime::from_intervals(intervals)) // support negatives. In order to tackle this, we try to convert the
// resulting value into an u64, which should obviously fail in the case
// that the value is below zero.
let intervals: u64 =
self.intervals().checked_sub(checked_dur2intervals(other)?)?.try_into().ok()?;
Some(SystemTime::from_intervals(intervals as i64))
} }
} }

View file

@ -35,6 +35,10 @@ impl Instant {
} }
impl SystemTime { impl SystemTime {
pub const MAX: SystemTime = SystemTime(Duration::MAX);
pub const MIN: SystemTime = SystemTime(Duration::ZERO);
pub fn now() -> SystemTime { pub fn now() -> SystemTime {
let result = blocking_scalar(systime_server(), GetUtcTimeMs.into()) let result = blocking_scalar(systime_server(), GetUtcTimeMs.into())
.expect("failed to request utc time in ms"); .expect("failed to request utc time in ms");

View file

@ -511,6 +511,83 @@ impl SystemTime {
#[stable(feature = "assoc_unix_epoch", since = "1.28.0")] #[stable(feature = "assoc_unix_epoch", since = "1.28.0")]
pub const UNIX_EPOCH: SystemTime = UNIX_EPOCH; pub const UNIX_EPOCH: SystemTime = UNIX_EPOCH;
/// Represents the maximum value representable by [`SystemTime`] on this platform.
///
/// This value differs a lot between platforms, but it is always the case
/// that any positive addition of a [`Duration`], whose value is greater
/// than or equal to the time precision of the operating system, to
/// [`SystemTime::MAX`] will fail.
///
/// # Examples
///
/// ```no_run
/// #![feature(time_systemtime_limits)]
/// use std::time::{Duration, SystemTime};
///
/// // Adding zero will change nothing.
/// assert_eq!(SystemTime::MAX.checked_add(Duration::ZERO), Some(SystemTime::MAX));
///
/// // But adding just one second will already fail ...
/// //
/// // Keep in mind that this in fact may succeed, if the Duration is
/// // smaller than the time precision of the operating system, which
/// // happens to be 1ns on most operating systems, with Windows being the
/// // notable exception by using 100ns, hence why this example uses 1s.
/// assert_eq!(SystemTime::MAX.checked_add(Duration::new(1, 0)), None);
///
/// // Utilize this for saturating arithmetic to improve error handling.
/// // In this case, we will use a certificate with a timestamp in the
/// // future as a practical example.
/// let configured_offset = Duration::from_secs(60 * 60 * 24);
/// let valid_after =
/// SystemTime::now()
/// .checked_add(configured_offset)
/// .unwrap_or(SystemTime::MAX);
/// ```
#[unstable(feature = "time_systemtime_limits", issue = "149067")]
pub const MAX: SystemTime = SystemTime(time::SystemTime::MAX);
/// Represents the minimum value representable by [`SystemTime`] on this platform.
///
/// This value differs a lot between platforms, but it is always the case
/// that any positive subtraction of a [`Duration`] from, whose value is
/// greater than or equal to the time precision of the operating system, to
/// [`SystemTime::MIN`] will fail.
///
/// Depending on the platform, this may be either less than or equal to
/// [`SystemTime::UNIX_EPOCH`], depending on whether the operating system
/// supports the representation of timestamps before the Unix epoch or not.
/// However, it is always guaranteed that a [`SystemTime::UNIX_EPOCH`] fits
/// between a [`SystemTime::MIN`] and [`SystemTime::MAX`].
///
/// # Examples
///
/// ```
/// #![feature(time_systemtime_limits)]
/// use std::time::{Duration, SystemTime};
///
/// // Subtracting zero will change nothing.
/// assert_eq!(SystemTime::MIN.checked_sub(Duration::ZERO), Some(SystemTime::MIN));
///
/// // But subtracting just one second will already fail.
/// //
/// // Keep in mind that this in fact may succeed, if the Duration is
/// // smaller than the time precision of the operating system, which
/// // happens to be 1ns on most operating systems, with Windows being the
/// // notable exception by using 100ns, hence why this example uses 1s.
/// assert_eq!(SystemTime::MIN.checked_sub(Duration::new(1, 0)), None);
///
/// // Utilize this for saturating arithmetic to improve error handling.
/// // In this case, we will use a cache expiry as a practical example.
/// let configured_expiry = Duration::from_secs(60 * 3);
/// let expiry_threshold =
/// SystemTime::now()
/// .checked_sub(configured_expiry)
/// .unwrap_or(SystemTime::MIN);
/// ```
#[unstable(feature = "time_systemtime_limits", issue = "149067")]
pub const MIN: SystemTime = SystemTime(time::SystemTime::MIN);
/// Returns the system time corresponding to "now". /// Returns the system time corresponding to "now".
/// ///
/// # Examples /// # Examples
@ -588,6 +665,9 @@ impl SystemTime {
/// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
/// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None` /// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise. /// otherwise.
///
/// In the case that the `duration` is smaller than the time precision of the operating
/// system, `Some(self)` will be returned.
#[stable(feature = "time_checked_add", since = "1.34.0")] #[stable(feature = "time_checked_add", since = "1.34.0")]
pub fn checked_add(&self, duration: Duration) -> Option<SystemTime> { pub fn checked_add(&self, duration: Duration) -> Option<SystemTime> {
self.0.checked_add_duration(&duration).map(SystemTime) self.0.checked_add_duration(&duration).map(SystemTime)
@ -596,6 +676,9 @@ impl SystemTime {
/// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
/// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None` /// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise. /// otherwise.
///
/// In the case that the `duration` is smaller than the time precision of the operating
/// system, `Some(self)` will be returned.
#[stable(feature = "time_checked_add", since = "1.34.0")] #[stable(feature = "time_checked_add", since = "1.34.0")]
pub fn checked_sub(&self, duration: Duration) -> Option<SystemTime> { pub fn checked_sub(&self, duration: Duration) -> Option<SystemTime> {
self.0.checked_sub_duration(&duration).map(SystemTime) self.0.checked_sub_duration(&duration).map(SystemTime)

View file

@ -1,4 +1,5 @@
#![feature(duration_constants)] #![feature(duration_constants)]
#![feature(time_systemtime_limits)]
use std::fmt::Debug; use std::fmt::Debug;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
@ -237,9 +238,34 @@ fn system_time_duration_since_max_range_on_unix() {
let min = SystemTime::UNIX_EPOCH - (Duration::new(i64::MAX as u64 + 1, 0)); let min = SystemTime::UNIX_EPOCH - (Duration::new(i64::MAX as u64 + 1, 0));
let max = SystemTime::UNIX_EPOCH + (Duration::new(i64::MAX as u64, 999_999_999)); let max = SystemTime::UNIX_EPOCH + (Duration::new(i64::MAX as u64, 999_999_999));
assert_eq!(min, SystemTime::MIN);
assert_eq!(max, SystemTime::MAX);
let delta_a = max.duration_since(min).expect("duration_since overflow"); let delta_a = max.duration_since(min).expect("duration_since overflow");
let delta_b = min.duration_since(max).expect_err("duration_since overflow").duration(); let delta_b = min.duration_since(max).expect_err("duration_since overflow").duration();
assert_eq!(Duration::MAX, delta_a); assert_eq!(Duration::MAX, delta_a);
assert_eq!(Duration::MAX, delta_b); assert_eq!(Duration::MAX, delta_b);
} }
#[test]
fn system_time_max_min() {
#[cfg(not(target_os = "windows"))]
/// Most (all?) non-Windows systems have nanosecond precision.
const MIN_INTERVAL: Duration = Duration::new(0, 1);
#[cfg(target_os = "windows")]
/// Windows' time precision is at 100ns.
const MIN_INTERVAL: Duration = Duration::new(0, 100);
// First, test everything with checked_* and Duration::ZERO.
assert_eq!(SystemTime::MAX.checked_add(Duration::ZERO), Some(SystemTime::MAX));
assert_eq!(SystemTime::MAX.checked_sub(Duration::ZERO), Some(SystemTime::MAX));
assert_eq!(SystemTime::MIN.checked_add(Duration::ZERO), Some(SystemTime::MIN));
assert_eq!(SystemTime::MIN.checked_sub(Duration::ZERO), Some(SystemTime::MIN));
// Now do the same again with checked_* but try by ± the lowest time precision.
assert!(SystemTime::MAX.checked_add(MIN_INTERVAL).is_none());
assert!(SystemTime::MAX.checked_sub(MIN_INTERVAL).is_some());
assert!(SystemTime::MIN.checked_add(MIN_INTERVAL).is_some());
assert!(SystemTime::MIN.checked_sub(MIN_INTERVAL).is_none());
}

@ -1 +1 @@
Subproject commit 8c0eacd5c4acbb650497454f3a58c9e8083202a4 Subproject commit 39aeceaa3aeab845bc4517e7a44e48727d3b9dbe

@ -1 +1 @@
Subproject commit 9cf5443d632673c4d41edad5e8ed8be86eeb3b8f Subproject commit c3c0f0b3da26610138b7ba7663f60cd2c68cf184

@ -1 +1 @@
Subproject commit 0fe83ab28985b99aba36a1f0dbde3e08286fefda Subproject commit 9fe8fa599ad228dda74f240cc32b54bc5c1aa3e6

@ -1 +1 @@
Subproject commit b14b4e40f53ca468beaf2f5d0dfb4f4c4ba6bc7b Subproject commit 50c5de90487b68d429a30cc9466dc8f5b410128f

@ -1 +1 @@
Subproject commit 111cfae2f9c3a43f7b0ff8fa68c51cc8f930637c Subproject commit 7d21279e40e8f0e91c2a22c5148dd2d745aef8b6

View file

@ -1,13 +1,9 @@
[book] [book]
multilingual = false
src = "src"
title = "The rustc book" title = "The rustc book"
[output.html] [output.html]
git-repository-url = "https://github.com/rust-lang/rust/tree/HEAD/src/doc/rustc" git-repository-url = "https://github.com/rust-lang/rust/tree/HEAD/src/doc/rustc"
edit-url-template = "https://github.com/rust-lang/rust/edit/HEAD/src/doc/rustc/{path}" edit-url-template = "https://github.com/rust-lang/rust/edit/HEAD/src/doc/rustc/{path}"
additional-css = ["theme/pagetoc.css"]
additional-js = ["theme/pagetoc.js"]
[output.html.search] [output.html.search]
use-boolean-and = true use-boolean-and = true

View file

@ -21,7 +21,7 @@ This target is cross-compiled. Dynamic linking is unsupported.
`#![no_std]` crates can be built using `build-std` to build `core` and `panic_abort` and optionally `alloc`. Unwinding panics are not yet supported on this target. `#![no_std]` crates can be built using `build-std` to build `core` and `panic_abort` and optionally `alloc`. Unwinding panics are not yet supported on this target.
`std` has only partial support due platform limitations. Notably: `std` has only partial support due to platform limitations. Notably:
- `std::process` and `std::net` are unimplemented. `std::thread` only supports sleeping and yielding, as this is a single-threaded environment. - `std::process` and `std::net` are unimplemented. `std::thread` only supports sleeping and yielding, as this is a single-threaded environment.
- `std::time` has full support for `Instant`, but no support for `SystemTime`. - `std::time` has full support for `Instant`, but no support for `SystemTime`.
- `std::io` has full support for `stdin`/`stdout`/`stderr`. `stdout` and `stderr` both write to to USB channel 1 on this platform and are not differentiated. - `std::io` has full support for `stdin`/`stdout`/`stderr`. `stdout` and `stderr` both write to to USB channel 1 on this platform and are not differentiated.

View file

@ -1,84 +0,0 @@
/* Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL) */
:root {
--toc-width: 270px;
--center-content-toc-shift: calc(-1 * var(--toc-width) / 2);
}
.nav-chapters {
/* adjust width of buttons that bring to the previous or the next page */
min-width: 50px;
}
@media only screen {
@media (max-width: 1179px) {
.sidebar-hidden #sidetoc {
display: none;
}
}
@media (max-width: 1439px) {
.sidebar-visible #sidetoc {
display: none;
}
}
@media (1180px <= width <= 1439px) {
.sidebar-hidden main {
position: relative;
left: var(--center-content-toc-shift);
}
}
@media (1440px <= width <= 1700px) {
.sidebar-visible main {
position: relative;
left: var(--center-content-toc-shift);
}
}
#sidetoc {
margin-left: calc(100% + 20px);
}
#pagetoc {
position: fixed;
/* adjust TOC width */
width: var(--toc-width);
height: calc(100vh - var(--menu-bar-height) - 0.67em * 4);
overflow: auto;
}
#pagetoc a {
border-left: 1px solid var(--sidebar-bg);
color: var(--fg);
display: block;
padding-bottom: 5px;
padding-top: 5px;
padding-left: 10px;
text-align: left;
text-decoration: none;
}
#pagetoc a:hover,
#pagetoc a.active {
background: var(--sidebar-bg);
color: var(--sidebar-active) !important;
}
#pagetoc .active {
background: var(--sidebar-bg);
color: var(--sidebar-active);
}
#pagetoc .pagetoc-H2 {
padding-left: 20px;
}
#pagetoc .pagetoc-H3 {
padding-left: 40px;
}
#pagetoc .pagetoc-H4 {
padding-left: 60px;
}
}
@media print {
#sidetoc {
display: none;
}
}

View file

@ -1,104 +0,0 @@
// Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL)
let activeHref = location.href;
function updatePageToc(elem = undefined) {
let selectedPageTocElem = elem;
const pagetoc = document.getElementById("pagetoc");
function getRect(element) {
return element.getBoundingClientRect();
}
function overflowTop(container, element) {
return getRect(container).top - getRect(element).top;
}
function overflowBottom(container, element) {
return getRect(container).bottom - getRect(element).bottom;
}
// We've not selected a heading to highlight, and the URL needs updating
// so we need to find a heading based on the URL
if (selectedPageTocElem === undefined && location.href !== activeHref) {
activeHref = location.href;
for (const pageTocElement of pagetoc.children) {
if (pageTocElement.href === activeHref) {
selectedPageTocElem = pageTocElement;
}
}
}
// We still don't have a selected heading, let's try and find the most
// suitable heading based on the scroll position
if (selectedPageTocElem === undefined) {
const margin = window.innerHeight / 3;
const headers = document.getElementsByClassName("header");
for (let i = 0; i < headers.length; i++) {
const header = headers[i];
if (selectedPageTocElem === undefined && getRect(header).top >= 0) {
if (getRect(header).top < margin) {
selectedPageTocElem = header;
} else {
selectedPageTocElem = headers[Math.max(0, i - 1)];
}
}
// a very long last section's heading is over the screen
if (selectedPageTocElem === undefined && i === headers.length - 1) {
selectedPageTocElem = header;
}
}
}
// Remove the active flag from all pagetoc elements
for (const pageTocElement of pagetoc.children) {
pageTocElement.classList.remove("active");
}
// If we have a selected heading, set it to active and scroll to it
if (selectedPageTocElem !== undefined) {
for (const pageTocElement of pagetoc.children) {
if (selectedPageTocElem.href.localeCompare(pageTocElement.href) === 0) {
pageTocElement.classList.add("active");
if (overflowTop(pagetoc, pageTocElement) > 0) {
pagetoc.scrollTop = pageTocElement.offsetTop;
}
if (overflowBottom(pagetoc, pageTocElement) < 0) {
pagetoc.scrollTop -= overflowBottom(pagetoc, pageTocElement);
}
}
}
}
}
if (document.getElementById("sidetoc") === null &&
document.getElementsByClassName("header").length > 0) {
// The sidetoc element doesn't exist yet, let's create it
// Create the empty sidetoc and pagetoc elements
const sidetoc = document.createElement("div");
const pagetoc = document.createElement("div");
sidetoc.id = "sidetoc";
pagetoc.id = "pagetoc";
sidetoc.appendChild(pagetoc);
// And append them to the current DOM
const main = document.querySelector('main');
main.insertBefore(sidetoc, main.firstChild);
// Populate sidebar on load
window.addEventListener("load", () => {
for (const header of document.getElementsByClassName("header")) {
const link = document.createElement("a");
link.innerHTML = header.innerHTML;
link.href = header.hash;
link.classList.add("pagetoc-" + header.parentElement.tagName);
document.getElementById("pagetoc").appendChild(link);
link.onclick = () => updatePageToc(link);
}
updatePageToc();
});
// Update page table of contents selected heading on scroll
window.addEventListener("scroll", () => updatePageToc());
}

View file

@ -4,7 +4,7 @@ Rustdoc's HTML output includes a settings menu, and this chapter describes what
each setting in this menu does. each setting in this menu does.
It can be accessed by clicking on the gear button It can be accessed by clicking on the gear button
(<i class="fa fa-cog" aria-hidden="true"></i>) in the upper right. (<i class="fas fa-gear" aria-hidden="true"></i>) in the upper right.
## Changing displayed theme ## Changing displayed theme

View file

@ -1,8 +1,6 @@
[book] [book]
title = "The Rust Style Guide" title = "The Rust Style Guide"
author = "The Rust Style Team" authors = ["The Rust Style Team"]
multilingual = false
src = "src"
[output.html] [output.html]
git-repository-url = "https://github.com/rust-lang/rust/tree/HEAD/src/doc/style-guide/" git-repository-url = "https://github.com/rust-lang/rust/tree/HEAD/src/doc/style-guide/"

View file

@ -1,6 +1,5 @@
[book] [book]
title = "The Rust Unstable Book" title = "The Rust Unstable Book"
author = "The Rust Community"
[output.html] [output.html]
git-repository-url = "https://github.com/rust-lang/rust/tree/HEAD/src/doc/unstable-book" git-repository-url = "https://github.com/rust-lang/rust/tree/HEAD/src/doc/unstable-book"

View file

@ -5,7 +5,8 @@ edition = "2021"
workspace = "../rustbook" workspace = "../rustbook"
[dependencies] [dependencies]
mdbook = { version = "0.4", default-features = false, features = ["search"] } mdbook-driver = { version = "0.5.1", features = ["search"] }
mdbook-summary = "0.5.1"
[[bin]] [[bin]]
name = "error_index_generator" name = "error_index_generator"

View file

@ -12,8 +12,10 @@ use std::io::Write;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use mdbook::book::{BookItem, Chapter, parse_summary}; use mdbook_driver::MDBook;
use mdbook::{Config, MDBook}; use mdbook_driver::book::{BookItem, Chapter};
use mdbook_driver::config::Config;
use mdbook_summary::parse_summary;
use rustc_errors::codes::DIAGNOSTICS; use rustc_errors::codes::DIAGNOSTICS;
enum OutputFormat { enum OutputFormat {
@ -121,7 +123,7 @@ This page lists all the error codes emitted by the Rust compiler.
source_path: None, source_path: None,
parent_names: Vec::new(), parent_names: Vec::new(),
}; };
book.book.sections.push(BookItem::Chapter(chapter)); book.book.items.push(BookItem::Chapter(chapter));
book.build()?; book.build()?;
// The error-index used to be generated manually (without mdbook), and the // The error-index used to be generated manually (without mdbook), and the

File diff suppressed because it is too large Load diff

View file

@ -8,14 +8,9 @@ license = "MIT OR Apache-2.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
clap = "4.0.32" clap = { version = "4.0.32", features = ["cargo"] }
env_logger = "0.11" mdbook-driver = { version = "0.5.2", features = ["search"] }
libc = "0.2" mdbook-i18n-helpers = "0.4.0"
mdbook-trpl = { path = "../../doc/book/packages/mdbook-trpl" }
mdbook-i18n-helpers = "0.3.3"
mdbook-spec = { path = "../../doc/reference/mdbook-spec" } mdbook-spec = { path = "../../doc/reference/mdbook-spec" }
mdbook-trpl = { path = "../../doc/book/packages/mdbook-trpl" }
[dependencies.mdbook] tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
version = "0.4.52"
default-features = false
features = ["search"]

View file

@ -2,15 +2,27 @@ use std::env;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use clap::{ArgMatches, Command, arg, crate_version}; use clap::{ArgMatches, Command, arg, crate_version};
use mdbook::MDBook; use mdbook_driver::MDBook;
use mdbook::errors::Result as Result3; use mdbook_driver::errors::Result as Result3;
use mdbook_i18n_helpers::preprocessors::Gettext; use mdbook_i18n_helpers::preprocessors::Gettext;
use mdbook_spec::Spec; use mdbook_spec::Spec;
use mdbook_trpl::{Figure, Listing, Note}; use mdbook_trpl::{Figure, Listing, Note};
fn main() { fn main() {
let crate_version = concat!("v", crate_version!()); let crate_version = concat!("v", crate_version!());
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init(); let filter = tracing_subscriber::EnvFilter::builder()
.with_env_var("MDBOOK_LOG")
.with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into())
.from_env_lossy();
tracing_subscriber::fmt()
.without_time()
.with_ansi(std::io::IsTerminal::is_terminal(&std::io::stderr()))
.with_writer(std::io::stderr)
.with_env_filter(filter)
.with_target(std::env::var_os("MDBOOK_LOG").is_some())
.init();
// env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
let d_arg = arg!(-d --"dest-dir" <DEST_DIR> let d_arg = arg!(-d --"dest-dir" <DEST_DIR>
"The output directory for your book\n(Defaults to ./book when omitted)") "The output directory for your book\n(Defaults to ./book when omitted)")
.required(false) .required(false)
@ -82,60 +94,22 @@ fn main() {
}; };
} }
// Build command implementation fn build(args: &ArgMatches) -> Result3<()> {
pub fn build(args: &ArgMatches) -> Result3<()> {
let book_dir = get_book_dir(args); let book_dir = get_book_dir(args);
let mut book = load_book(&book_dir)?; let dest_dir = args.get_one::<PathBuf>("dest-dir");
let lang = args.get_one::<String>("lang");
if let Some(lang) = args.get_one::<String>("lang") { let rust_root = args.get_one::<PathBuf>("rust-root");
let gettext = Gettext; let book = load_book(&book_dir, dest_dir, lang, rust_root.cloned())?;
book.with_preprocessor(gettext); book.build()
book.config.set("book.language", lang).unwrap();
}
// Set this to allow us to catch bugs in advance.
book.config.build.create_missing = false;
if let Some(dest_dir) = args.get_one::<PathBuf>("dest-dir") {
book.config.build.build_dir = dest_dir.into();
}
// NOTE: Replacing preprocessors using this technique causes error
// messages to be displayed when the original preprocessor doesn't work
// (but it otherwise succeeds).
//
// This should probably be fixed in mdbook to remove the existing
// preprocessor, or this should modify the config and use
// MDBook::load_with_config.
if book.config.get_preprocessor("trpl-note").is_some() {
book.with_preprocessor(Note);
}
if book.config.get_preprocessor("trpl-listing").is_some() {
book.with_preprocessor(Listing);
}
if book.config.get_preprocessor("trpl-figure").is_some() {
book.with_preprocessor(Figure);
}
if book.config.get_preprocessor("spec").is_some() {
let rust_root = args.get_one::<PathBuf>("rust-root").cloned();
book.with_preprocessor(Spec::new(rust_root)?);
}
book.build()?;
Ok(())
} }
fn test(args: &ArgMatches) -> Result3<()> { fn test(args: &ArgMatches) -> Result3<()> {
let book_dir = get_book_dir(args); let book_dir = get_book_dir(args);
let mut book = load_book(&book_dir, None, None, None)?;
let library_paths = args let library_paths = args
.try_get_one::<Vec<String>>("library-path")? .try_get_one::<Vec<String>>("library-path")?
.map(|v| v.iter().map(|s| s.as_str()).collect::<Vec<&str>>()) .map(|v| v.iter().map(|s| s.as_str()).collect::<Vec<&str>>())
.unwrap_or_default(); .unwrap_or_default();
let mut book = load_book(&book_dir)?;
book.test(library_paths) book.test(library_paths)
} }
@ -148,10 +122,52 @@ fn get_book_dir(args: &ArgMatches) -> PathBuf {
} }
} }
fn load_book(book_dir: &Path) -> Result3<MDBook> { fn load_book(
book_dir: &Path,
dest_dir: Option<&PathBuf>,
lang: Option<&String>,
rust_root: Option<PathBuf>,
) -> Result3<MDBook> {
let mut book = MDBook::load(book_dir)?; let mut book = MDBook::load(book_dir)?;
book.config.set("output.html.input-404", "").unwrap(); book.config.set("output.html.input-404", "").unwrap();
book.config.set("output.html.hash-files", true).unwrap(); book.config.set("output.html.hash-files", true).unwrap();
if let Some(lang) = lang {
let gettext = Gettext;
book.with_preprocessor(gettext);
book.config.set("book.language", lang).unwrap();
}
// Set this to allow us to catch bugs in advance.
book.config.build.create_missing = false;
if let Some(dest_dir) = dest_dir {
book.config.build.build_dir = dest_dir.into();
}
// NOTE: Replacing preprocessors using this technique causes error
// messages to be displayed when the original preprocessor doesn't work
// (but it otherwise succeeds).
//
// This should probably be fixed in mdbook to remove the existing
// preprocessor, or this should modify the config and use
// MDBook::load_with_config.
if book.config.contains_key("preprocessor.trpl-note") {
book.with_preprocessor(Note);
}
if book.config.contains_key("preprocessor.trpl-listing") {
book.with_preprocessor(Listing);
}
if book.config.contains_key("preprocessor.trpl-figure") {
book.with_preprocessor(Figure);
}
if book.config.contains_key("preprocessor.spec") {
book.with_preprocessor(Spec::new(rust_root)?);
}
Ok(book) Ok(book)
} }
@ -159,7 +175,7 @@ fn parse_library_paths(input: &str) -> Result<Vec<String>, String> {
Ok(input.split(",").map(String::from).collect()) Ok(input.split(",").map(String::from).collect())
} }
fn handle_error(error: mdbook::errors::Error) -> ! { fn handle_error(error: mdbook_driver::errors::Error) -> ! {
eprintln!("Error: {}", error); eprintln!("Error: {}", error);
for cause in error.chain().skip(1) { for cause in error.chain().skip(1) {

View file

@ -1,6 +1,7 @@
//! Checks the licenses of third-party dependencies. //! Checks the licenses of third-party dependencies.
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter};
use std::fs::{File, read_dir}; use std::fs::{File, read_dir};
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
@ -14,6 +15,25 @@ use crate::diagnostics::{RunningCheck, TidyCtx};
#[path = "../../../bootstrap/src/utils/proc_macro_deps.rs"] #[path = "../../../bootstrap/src/utils/proc_macro_deps.rs"]
mod proc_macro_deps; mod proc_macro_deps;
#[derive(Clone, Copy)]
struct ListLocation {
path: &'static str,
line: u32,
}
impl Display for ListLocation {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.path, self.line)
}
}
/// Creates a [`ListLocation`] for the current location (with an additional offset to the actual list start);
macro_rules! location {
(+ $offset:literal) => {
ListLocation { path: file!(), line: line!() + $offset }
};
}
/// These are licenses that are allowed for all crates, including the runtime, /// These are licenses that are allowed for all crates, including the runtime,
/// rustc, tools, etc. /// rustc, tools, etc.
#[rustfmt::skip] #[rustfmt::skip]
@ -87,6 +107,8 @@ pub(crate) struct WorkspaceInfo<'a> {
pub(crate) submodules: &'a [&'a str], pub(crate) submodules: &'a [&'a str],
} }
const WORKSPACE_LOCATION: ListLocation = location!(+4);
/// The workspaces to check for licensing and optionally permitted dependencies. /// The workspaces to check for licensing and optionally permitted dependencies.
// FIXME auto detect all cargo workspaces // FIXME auto detect all cargo workspaces
pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[
@ -222,10 +244,14 @@ const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[
const EXCEPTIONS_RUSTBOOK: ExceptionList = &[ const EXCEPTIONS_RUSTBOOK: ExceptionList = &[
// tidy-alphabetical-start // tidy-alphabetical-start
("cssparser", "MPL-2.0"), ("font-awesome-as-a-crate", "CC-BY-4.0 AND MIT"),
("cssparser-macros", "MPL-2.0"), ("mdbook-core", "MPL-2.0"),
("dtoa-short", "MPL-2.0"), ("mdbook-driver", "MPL-2.0"),
("mdbook", "MPL-2.0"), ("mdbook-html", "MPL-2.0"),
("mdbook-markdown", "MPL-2.0"),
("mdbook-preprocessor", "MPL-2.0"),
("mdbook-renderer", "MPL-2.0"),
("mdbook-summary", "MPL-2.0"),
// tidy-alphabetical-end // tidy-alphabetical-end
]; ];
@ -242,19 +268,6 @@ const EXCEPTIONS_BOOTSTRAP: ExceptionList = &[];
const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[]; const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[];
#[derive(Clone, Copy)]
struct ListLocation {
path: &'static str,
line: u32,
}
/// Creates a [`ListLocation`] for the current location (with an additional offset to the actual list start);
macro_rules! location {
(+ $offset:literal) => {
ListLocation { path: file!(), line: line!() + $offset }
};
}
const PERMITTED_RUSTC_DEPS_LOCATION: ListLocation = location!(+6); const PERMITTED_RUSTC_DEPS_LOCATION: ListLocation = location!(+6);
/// Crates rustc is allowed to depend on. Avoid adding to the list if possible. /// Crates rustc is allowed to depend on. Avoid adding to the list if possible.
@ -641,6 +654,13 @@ pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) {
.other_options(vec!["--locked".to_owned()]); .other_options(vec!["--locked".to_owned()]);
let metadata = t!(cmd.exec()); let metadata = t!(cmd.exec());
// Check for packages which have been moved into a different workspace and not updated
let absolute_root =
if path == "." { root.to_path_buf() } else { t!(std::path::absolute(root.join(path))) };
let absolute_root_real = t!(std::path::absolute(&metadata.workspace_root));
if absolute_root_real != absolute_root {
check.error(format!("{path} is part of another workspace ({} != {}), remove from `WORKSPACES` ({WORKSPACE_LOCATION})", absolute_root.display(), absolute_root_real.display()));
}
check_license_exceptions(&metadata, path, exceptions, &mut check); check_license_exceptions(&metadata, path, exceptions, &mut check);
if let Some((crates, permitted_deps, location)) = crates_and_deps { if let Some((crates, permitted_deps, location)) = crates_and_deps {
let descr = crates.get(0).unwrap_or(&path); let descr = crates.get(0).unwrap_or(&path);

View file

@ -22,15 +22,15 @@ pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) {
// CHECK-LABEL: use_foreign_c_variadic_1_1 // CHECK-LABEL: use_foreign_c_variadic_1_1
pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) { pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) {
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, i32 noundef 42) // CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, i32 noundef{{( signext)?}} 42)
foreign_c_variadic_1(ap, 42i32); foreign_c_variadic_1(ap, 42i32);
} }
pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) { pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) {
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, i32 noundef 2, i32 noundef 42) // CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, i32 noundef{{( signext)?}} 2, i32 noundef{{( signext)?}} 42)
foreign_c_variadic_1(ap, 2i32, 42i32); foreign_c_variadic_1(ap, 2i32, 42i32);
} }
pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) { pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) {
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, i32 noundef 2, i32 noundef 42, i32 noundef 0) // CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, i32 noundef{{( signext)?}} 2, i32 noundef{{( signext)?}} 42, i32 noundef{{( signext)?}} 0)
foreign_c_variadic_1(ap, 2i32, 42i32, 0i32); foreign_c_variadic_1(ap, 2i32, 42i32, 0i32);
} }

View file

@ -96,6 +96,17 @@ Thanks! <3
""" """
label = "O-ARM" label = "O-ARM"
[ping.loongarch]
message = """\
Hey LoongArch Group! This bug has been identified as a good "LoongArch candidate".
In case it's useful, here are some [instructions] for tackling these sorts of
bugs. Maybe take a look?
Thanks! <3
[instructions]: https://rustc-dev-guide.rust-lang.org/notification-groups/loongarch.html
"""
label = "O-loongarch"
[ping.risc-v] [ping.risc-v]
message = """\ message = """\
Hey RISC-V Group! This bug has been identified as a good "RISC-V candidate". Hey RISC-V Group! This bug has been identified as a good "RISC-V candidate".