From d5e6b6f8cc404ee5d3c1a08c6d2ef63fa96862fa Mon Sep 17 00:00:00 2001 From: joboet Date: Fri, 21 Mar 2025 19:42:17 +0100 Subject: [PATCH 01/12] core: simplify `Extend` for tuples This is an alternative to #137400. The current macro is incredibly complicated and introduces subtle bugs like calling the `extend_one` of the individual collections in backwards order. This PR drastically simplifies the macro by removing recursion and moving the specialization out of the macro. It also fixes the ordering issue described above (I've stolen the test of the new behaviour from #137400). Additionally, the 1-tuple is now special-cased to allow taking advantage of the well-optimized `Extend` implementations of the individual collection. --- library/core/src/iter/traits/collect.rs | 432 +++++++++++++----------- 1 file changed, 236 insertions(+), 196 deletions(-) diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 97bb21c8a36e8..b4ab81d15a330 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -459,234 +459,274 @@ impl Extend<()> for () { fn extend_one(&mut self, _item: ()) {} } -macro_rules! spec_tuple_impl { - ( - ( - $ty_name:ident, $var_name:ident, $extend_ty_name: ident, - $trait_name:ident, $default_fn_name:ident, $cnt:tt - ), - ) => { - spec_tuple_impl!( - $trait_name, - $default_fn_name, - #[doc(fake_variadic)] - #[doc = "This trait is implemented for tuples up to twelve items long. The `impl`s for \ - 1- and 3- through 12-ary tuples were stabilized after 2-tuples, in \ - 1.85.0."] - => ($ty_name, $var_name, $extend_ty_name, $cnt), - ); - }; - ( - ( - $ty_name:ident, $var_name:ident, $extend_ty_name: ident, - $trait_name:ident, $default_fn_name:ident, $cnt:tt - ), - $( - ( - $ty_names:ident, $var_names:ident, $extend_ty_names:ident, - $trait_names:ident, $default_fn_names:ident, $cnts:tt - ), - )* - ) => { - spec_tuple_impl!( - $( - ( - $ty_names, $var_names, $extend_ty_names, - $trait_names, $default_fn_names, $cnts - ), - )* - ); - spec_tuple_impl!( - $trait_name, - $default_fn_name, - #[doc(hidden)] - => ( - $ty_name, $var_name, $extend_ty_name, $cnt - ), - $( - ( - $ty_names, $var_names, $extend_ty_names, $cnts - ), - )* - ); - }; - ( - $trait_name:ident, $default_fn_name:ident, #[$meta:meta] - $(#[$doctext:meta])? => $( - ( - $ty_names:ident, $var_names:ident, $extend_ty_names:ident, $cnts:tt - ), - )* - ) => { - #[$meta] - $(#[$doctext])? - #[stable(feature = "extend_for_tuple", since = "1.56.0")] - impl<$($ty_names,)* $($extend_ty_names,)*> Extend<($($ty_names,)*)> for ($($extend_ty_names,)*) - where - $($extend_ty_names: Extend<$ty_names>,)* - { - /// Allows to `extend` a tuple of collections that also implement `Extend`. - /// - /// See also: [`Iterator::unzip`] - /// - /// # Examples - /// ``` - /// // Example given for a 2-tuple, but 1- through 12-tuples are supported - /// let mut tuple = (vec![0], vec![1]); - /// tuple.extend([(2, 3), (4, 5), (6, 7)]); - /// assert_eq!(tuple.0, [0, 2, 4, 6]); - /// assert_eq!(tuple.1, [1, 3, 5, 7]); - /// - /// // also allows for arbitrarily nested tuples as elements - /// let mut nested_tuple = (vec![1], (vec![2], vec![3])); - /// nested_tuple.extend([(4, (5, 6)), (7, (8, 9))]); - /// - /// let (a, (b, c)) = nested_tuple; - /// assert_eq!(a, [1, 4, 7]); - /// assert_eq!(b, [2, 5, 8]); - /// assert_eq!(c, [3, 6, 9]); - /// ``` - fn extend>(&mut self, into_iter: T) { - let ($($var_names,)*) = self; - let iter = into_iter.into_iter(); - $trait_name::extend(iter, $($var_names,)*); - } +/// This trait is implemented for tuples up to twelve items long. The `impl`s for +/// 1- and 3- through 12-ary tuples were stabilized after 2-tuples, in 1.85.0. +#[doc(fake_variadic)] // the other implementations are below. +#[stable(feature = "extend_for_tuple", since = "1.56.0")] +impl Extend<(T,)> for (ExtendT,) +where + ExtendT: Extend, +{ + /// Allows to `extend` a tuple of collections that also implement `Extend`. + /// + /// See also: [`Iterator::unzip`] + /// + /// # Examples + /// ``` + /// // Example given for a 2-tuple, but 1- through 12-tuples are supported + /// let mut tuple = (vec![0], vec![1]); + /// tuple.extend([(2, 3), (4, 5), (6, 7)]); + /// assert_eq!(tuple.0, [0, 2, 4, 6]); + /// assert_eq!(tuple.1, [1, 3, 5, 7]); + /// + /// // also allows for arbitrarily nested tuples as elements + /// let mut nested_tuple = (vec![1], (vec![2], vec![3])); + /// nested_tuple.extend([(4, (5, 6)), (7, (8, 9))]); + /// + /// let (a, (b, c)) = nested_tuple; + /// assert_eq!(a, [1, 4, 7]); + /// assert_eq!(b, [2, 5, 8]); + /// assert_eq!(c, [3, 6, 9]); + /// ``` + fn extend>(&mut self, iter: I) { + self.0.extend(iter.into_iter().map(|t| t.0)); + } - fn extend_one(&mut self, item: ($($ty_names,)*)) { - $(self.$cnts.extend_one(item.$cnts);)* - } + fn extend_one(&mut self, item: (T,)) { + self.0.extend_one(item.0) + } - fn extend_reserve(&mut self, additional: usize) { - $(self.$cnts.extend_reserve(additional);)* - } + fn extend_reserve(&mut self, additional: usize) { + self.0.extend_reserve(additional) + } - unsafe fn extend_one_unchecked(&mut self, item: ($($ty_names,)*)) { - // SAFETY: Those are our safety preconditions, and we correctly forward `extend_reserve`. - unsafe { - $(self.$cnts.extend_one_unchecked(item.$cnts);)* - } - } - } + unsafe fn extend_one_unchecked(&mut self, item: (T,)) { + // SAFETY: the caller guarantees all preconditions. + unsafe { self.0.extend_one_unchecked(item.0) } + } +} - trait $trait_name<$($ty_names),*> { - fn extend(self, $($var_names: &mut $ty_names,)*); - } +/// This implementation turns an iterator of tuples into a tuple of types which implement +/// [`Default`] and [`Extend`]. +/// +/// This is similar to [`Iterator::unzip`], but is also composable with other [`FromIterator`] +/// implementations: +/// +/// ```rust +/// # fn main() -> Result<(), core::num::ParseIntError> { +/// let string = "1,2,123,4"; +/// +/// // Example given for a 2-tuple, but 1- through 12-tuples are supported +/// let (numbers, lengths): (Vec<_>, Vec<_>) = string +/// .split(',') +/// .map(|s| s.parse().map(|n: u32| (n, s.len()))) +/// .collect::>()?; +/// +/// assert_eq!(numbers, [1, 2, 123, 4]); +/// assert_eq!(lengths, [1, 1, 3, 1]); +/// # Ok(()) } +/// ``` +#[doc(fake_variadic)] // the other implementations are below. +#[stable(feature = "from_iterator_for_tuple", since = "1.79.0")] +impl FromIterator<(T,)> for (ExtendT,) +where + ExtendT: Default + Extend, +{ + fn from_iter>(iter: Iter) -> Self { + let mut res = ExtendT::default(); + res.extend(iter.into_iter().map(|t| t.0)); + (res,) + } +} - fn $default_fn_name<$($ty_names,)* $($extend_ty_names,)*>( - iter: impl Iterator, - $($var_names: &mut $extend_ty_names,)* - ) where - $($extend_ty_names: Extend<$ty_names>,)* - { - fn extend<'a, $($ty_names,)*>( - $($var_names: &'a mut impl Extend<$ty_names>,)* - ) -> impl FnMut((), ($($ty_names,)*)) + 'a { - #[allow(non_snake_case)] - move |(), ($($extend_ty_names,)*)| { - $($var_names.extend_one($extend_ty_names);)* - } - } +/// An implementation of [`extend`](Extend::extend) that calls `extend_one` or +/// `extend_one_unchecked` for each element of the iterator. +fn default_extend(collection: &mut ExtendT, iter: I) +where + ExtendT: Extend, + I: IntoIterator, +{ + // Specialize on `TrustedLen` and call `extend_one_unchecked` where + // applicable. + trait SpecExtend { + fn extend(&mut self, iter: I); + } + // Extracting these to separate functions avoid monomorphising the closures + // for every iterator type. + fn extender(collection: &mut ExtendT) -> impl FnMut(T) + use<'_, ExtendT, T> + where + ExtendT: Extend, + { + move |item| collection.extend_one(item) + } + + unsafe fn unchecked_extender( + collection: &mut ExtendT, + ) -> impl FnMut(T) + use<'_, ExtendT, T> + where + ExtendT: Extend, + { + // SAFETY: we make sure that there is enough space at the callsite of + // this function. + move |item| unsafe { collection.extend_one_unchecked(item) } + } + + impl SpecExtend for ExtendT + where + ExtendT: Extend, + I: Iterator, + { + default fn extend(&mut self, iter: I) { let (lower_bound, _) = iter.size_hint(); if lower_bound > 0 { - $($var_names.extend_reserve(lower_bound);)* + self.extend_reserve(lower_bound); } - iter.fold((), extend($($var_names,)*)); + iter.for_each(extender(self)) } + } - impl<$($ty_names,)* $($extend_ty_names,)* Iter> $trait_name<$($extend_ty_names),*> for Iter - where - $($extend_ty_names: Extend<$ty_names>,)* - Iter: Iterator, - { - default fn extend(self, $($var_names: &mut $extend_ty_names),*) { - $default_fn_name(self, $($var_names),*); + impl SpecExtend for ExtendT + where + ExtendT: Extend, + I: TrustedLen, + { + fn extend(&mut self, iter: I) { + let (lower_bound, upper_bound) = iter.size_hint(); + if lower_bound > 0 { + self.extend_reserve(lower_bound); + } + + if upper_bound.is_none() { + // We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway. + iter.for_each(extender(self)) + } else { + // SAFETY: We reserve enough space for the `size_hint`, and the iterator is + // `TrustedLen` so its `size_hint` is exact. + iter.for_each(unsafe { unchecked_extender(self) }) } } + } - impl<$($ty_names,)* $($extend_ty_names,)* Iter> $trait_name<$($extend_ty_names),*> for Iter + SpecExtend::extend(collection, iter.into_iter()); +} + +// Implements `Extend` and `FromIterator` for tuples with length larger than one. +macro_rules! impl_extend_tuple { + ($(($ty:tt, $extend_ty:tt, $index:tt)),+) => { + #[doc(hidden)] + #[stable(feature = "extend_for_tuple", since = "1.56.0")] + impl<$($ty,)+ $($extend_ty,)+> Extend<($($ty,)+)> for ($($extend_ty,)+) where - $($extend_ty_names: Extend<$ty_names>,)* - Iter: TrustedLen, + $($extend_ty: Extend<$ty>,)+ { - fn extend(self, $($var_names: &mut $extend_ty_names,)*) { - fn extend<'a, $($ty_names,)*>( - $($var_names: &'a mut impl Extend<$ty_names>,)* - ) -> impl FnMut((), ($($ty_names,)*)) + 'a { - #[allow(non_snake_case)] - // SAFETY: We reserve enough space for the `size_hint`, and the iterator is - // `TrustedLen` so its `size_hint` is exact. - move |(), ($($extend_ty_names,)*)| unsafe { - $($var_names.extend_one_unchecked($extend_ty_names);)* - } - } + fn extend>(&mut self, iter: T) { + default_extend(self, iter) + } - let (lower_bound, upper_bound) = self.size_hint(); + fn extend_one(&mut self, item: ($($ty,)+)) { + $(self.$index.extend_one(item.$index);)+ + } - if upper_bound.is_none() { - // We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway. - $default_fn_name(self, $($var_names,)*); - return; - } + fn extend_reserve(&mut self, additional: usize) { + $(self.$index.extend_reserve(additional);)+ + } - if lower_bound > 0 { - $($var_names.extend_reserve(lower_bound);)* + unsafe fn extend_one_unchecked(&mut self, item: ($($ty,)+)) { + // SAFETY: Those are our safety preconditions, and we correctly forward `extend_reserve`. + unsafe { + $(self.$index.extend_one_unchecked(item.$index);)+ } - - self.fold((), extend($($var_names,)*)); } } - /// This implementation turns an iterator of tuples into a tuple of types which implement - /// [`Default`] and [`Extend`]. - /// - /// This is similar to [`Iterator::unzip`], but is also composable with other [`FromIterator`] - /// implementations: - /// - /// ```rust - /// # fn main() -> Result<(), core::num::ParseIntError> { - /// let string = "1,2,123,4"; - /// - /// // Example given for a 2-tuple, but 1- through 12-tuples are supported - /// let (numbers, lengths): (Vec<_>, Vec<_>) = string - /// .split(',') - /// .map(|s| s.parse().map(|n: u32| (n, s.len()))) - /// .collect::>()?; - /// - /// assert_eq!(numbers, [1, 2, 123, 4]); - /// assert_eq!(lengths, [1, 1, 3, 1]); - /// # Ok(()) } - /// ``` - #[$meta] - $(#[$doctext])? + #[doc(hidden)] #[stable(feature = "from_iterator_for_tuple", since = "1.79.0")] - impl<$($ty_names,)* $($extend_ty_names,)*> FromIterator<($($extend_ty_names,)*)> for ($($ty_names,)*) + impl<$($ty,)+ $($extend_ty,)+> FromIterator<($($ty,)+)> for ($($extend_ty,)+) where - $($ty_names: Default + Extend<$extend_ty_names>,)* + $($extend_ty: Default + Extend<$ty>,)+ { - fn from_iter>(iter: Iter) -> Self { - let mut res = <($($ty_names,)*)>::default(); + fn from_iter>(iter: Iter) -> Self { + let mut res = Self::default(); res.extend(iter); - res } } - }; } -spec_tuple_impl!( - (L, l, EL, TraitL, default_extend_tuple_l, 11), - (K, k, EK, TraitK, default_extend_tuple_k, 10), - (J, j, EJ, TraitJ, default_extend_tuple_j, 9), - (I, i, EI, TraitI, default_extend_tuple_i, 8), - (H, h, EH, TraitH, default_extend_tuple_h, 7), - (G, g, EG, TraitG, default_extend_tuple_g, 6), - (F, f, EF, TraitF, default_extend_tuple_f, 5), - (E, e, EE, TraitE, default_extend_tuple_e, 4), - (D, d, ED, TraitD, default_extend_tuple_d, 3), - (C, c, EC, TraitC, default_extend_tuple_c, 2), - (B, b, EB, TraitB, default_extend_tuple_b, 1), - (A, a, EA, TraitA, default_extend_tuple_a, 0), +impl_extend_tuple!((A, ExA, 0), (B, ExB, 1)); +impl_extend_tuple!((A, ExA, 0), (B, ExB, 1), (C, ExC, 2)); +impl_extend_tuple!((A, ExA, 0), (B, ExB, 1), (C, ExC, 2), (D, ExD, 3)); +impl_extend_tuple!((A, ExA, 0), (B, ExB, 1), (C, ExC, 2), (D, ExD, 3), (E, ExE, 4)); +impl_extend_tuple!((A, ExA, 0), (B, ExB, 1), (C, ExC, 2), (D, ExD, 3), (E, ExE, 4), (F, ExF, 5)); +impl_extend_tuple!( + (A, ExA, 0), + (B, ExB, 1), + (C, ExC, 2), + (D, ExD, 3), + (E, ExE, 4), + (F, ExF, 5), + (G, ExG, 6) +); +impl_extend_tuple!( + (A, ExA, 0), + (B, ExB, 1), + (C, ExC, 2), + (D, ExD, 3), + (E, ExE, 4), + (F, ExF, 5), + (G, ExG, 6), + (H, ExH, 7) +); +impl_extend_tuple!( + (A, ExA, 0), + (B, ExB, 1), + (C, ExC, 2), + (D, ExD, 3), + (E, ExE, 4), + (F, ExF, 5), + (G, ExG, 6), + (H, ExH, 7), + (I, ExI, 8) +); +impl_extend_tuple!( + (A, ExA, 0), + (B, ExB, 1), + (C, ExC, 2), + (D, ExD, 3), + (E, ExE, 4), + (F, ExF, 5), + (G, ExG, 6), + (H, ExH, 7), + (I, ExI, 8), + (J, ExJ, 9) +); +impl_extend_tuple!( + (A, ExA, 0), + (B, ExB, 1), + (C, ExC, 2), + (D, ExD, 3), + (E, ExE, 4), + (F, ExF, 5), + (G, ExG, 6), + (H, ExH, 7), + (I, ExI, 8), + (J, ExJ, 9), + (K, ExK, 10) +); +impl_extend_tuple!( + (A, ExA, 0), + (B, ExB, 1), + (C, ExC, 2), + (D, ExD, 3), + (E, ExE, 4), + (F, ExF, 5), + (G, ExG, 6), + (H, ExH, 7), + (I, ExI, 8), + (J, ExJ, 9), + (K, ExK, 10), + (L, ExL, 11) ); From af66ece8221eb0a20fa17407182f6d01739bf8c9 Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Sat, 22 Feb 2025 01:09:51 +0100 Subject: [PATCH 02/12] Add tests for `Extend<(T, U)> for (ExtendT, ExtendU)` ordering of side-effects to `coretest`. --- .../coretests/tests/iter/traits/iterator.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/library/coretests/tests/iter/traits/iterator.rs b/library/coretests/tests/iter/traits/iterator.rs index e31d2e15b6d7e..5ef1f797ae55d 100644 --- a/library/coretests/tests/iter/traits/iterator.rs +++ b/library/coretests/tests/iter/traits/iterator.rs @@ -1,3 +1,5 @@ +use core::cell::RefCell; +use core::iter::zip; use core::num::NonZero; /// A wrapper struct that implements `Eq` and `Ord` based on the wrapped @@ -642,6 +644,26 @@ fn test_collect_for_tuples() { assert!(e.2 == d); } +#[test] +fn test_extend_for_tuple_side_effects_order() { + struct TrackingExtender<'a, T>(&'static str, &'a RefCell)>>, Vec); + impl Extend for TrackingExtender<'_, T> { + fn extend>(&mut self, i: I) { + let items = Vec::from_iter(i); + self.1.borrow_mut().push((self.0, items.clone())); + self.2.extend(items); + } + } + + let effects = RefCell::new(vec![]); + let l = TrackingExtender("l", &effects, vec![]); + let r = TrackingExtender("r", &effects, vec![]); + let mut p = ((l, r), ()); + p.extend(zip([(1, 2), (3, 4)], [(), ()])); + let effects = effects.into_inner(); + assert_eq!(effects, [("l", vec![1]), ("r", vec![2]), ("l", vec![3]), ("r", vec![4])]); +} + // just tests by whether or not this compiles fn _empty_impl_all_auto_traits() { use std::panic::{RefUnwindSafe, UnwindSafe}; From 6eef2859c73592967222f13c2d0ad599be75fe6b Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 1 Oct 2025 18:20:01 +0000 Subject: [PATCH 03/12] Add an ACP list item to the library tracking issue template Most new API has an associated ACP that is useful to reference, but it doesn't appear anywhere on the template for new tracking issues. Update this template to include a link to the ACP. --- .github/ISSUE_TEMPLATE/library_tracking_issue.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/library_tracking_issue.md b/.github/ISSUE_TEMPLATE/library_tracking_issue.md index d56da9d5d025a..de823dc300d15 100644 --- a/.github/ISSUE_TEMPLATE/library_tracking_issue.md +++ b/.github/ISSUE_TEMPLATE/library_tracking_issue.md @@ -51,6 +51,7 @@ If the feature is changed later, please add those PRs here as well. (Remember to update the `S-tracking-*` label when checking boxes.) +- [ ] ACP: rust-lang/libs-team#... - [ ] Implementation: #... - [ ] Final comment period (FCP)[^1] - [ ] Stabilization PR From 8fcb7e18c108294b06fd4c27ab6f007d68a7ef1b Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Wed, 1 Oct 2025 21:22:53 +0000 Subject: [PATCH 04/12] extended doc comment --- library/alloc/src/collections/btree/map.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index adcb444d08c66..ca5b46c9b0fd0 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1368,7 +1368,8 @@ impl BTreeMap { } /// Splits the collection into two at the given key. Returns everything after the given key, - /// including the key. + /// including the key. If the key is not present, the split will occur at the nearest + /// greater key, or return an empty map if no such key exists. /// /// # Examples /// From c33b667e8bc6623437c17e85519583796e38a1ae Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 6 Oct 2025 17:19:07 +1100 Subject: [PATCH 05/12] Hoist stranded `use` declarations --- compiler/rustc_middle/src/query/mod.rs | 4 ++-- compiler/rustc_middle/src/query/plumbing.rs | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 895c8c0295a04..30c4b4369830d 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -103,6 +103,8 @@ use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_target::spec::{PanicStrategy, SanitizerSet}; use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; +pub use self::keys::{AsLocalKey, Key, LocalCrate}; +pub use self::plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::metadata::ModChild; @@ -146,11 +148,9 @@ use crate::{dep_graph, mir, thir}; mod arena_cached; pub mod erase; mod keys; -pub use keys::{AsLocalKey, Key, LocalCrate}; pub mod on_disk_cache; #[macro_use] pub mod plumbing; -pub use plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; // Each of these queries corresponds to a function pointer field in the // `Providers` struct for requesting a value of that type, and a method diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 769df1ffd6f91..16cc81dacad4b 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -9,7 +9,9 @@ use rustc_query_system::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; pub(crate) use rustc_query_system::query::QueryJobId; use rustc_query_system::query::*; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +pub use sealed::IntoQueryParam; +use super::erase::EraseType; use crate::dep_graph; use crate::dep_graph::DepKind; use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; @@ -694,10 +696,6 @@ mod sealed { } } -pub use sealed::IntoQueryParam; - -use super::erase::EraseType; - #[derive(Copy, Clone, Debug, HashStable)] pub struct CyclePlaceholder(pub ErrorGuaranteed); From b97bdcb0a03abe95db5cdb4ea685ee320ac8ef29 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 8 Oct 2025 17:39:21 +1100 Subject: [PATCH 06/12] Extract query-method inner functions to `query::inner` --- compiler/rustc_middle/src/query/inner.rs | 78 ++++++++++++++++++++ compiler/rustc_middle/src/query/mod.rs | 5 +- compiler/rustc_middle/src/query/plumbing.rs | 82 +++------------------ 3 files changed, 90 insertions(+), 75 deletions(-) create mode 100644 compiler/rustc_middle/src/query/inner.rs diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs new file mode 100644 index 0000000000000..6efe21bc89976 --- /dev/null +++ b/compiler/rustc_middle/src/query/inner.rs @@ -0,0 +1,78 @@ +//! Helper functions that serve as the immediate implementation of +//! `tcx.$query(..)` and its variations. + +use rustc_query_system::query::{QueryCache, QueryMode, try_get_cached}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; + +use crate::query::IntoQueryParam; +use crate::query::erase::{self, Erase, EraseType}; +use crate::ty::TyCtxt; + +/// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)` +/// for all queries. +#[inline(always)] +pub(crate) fn query_get_at<'tcx, Cache>( + tcx: TyCtxt<'tcx>, + execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, + query_cache: &Cache, + span: Span, + key: Cache::Key, +) -> Cache::Value +where + Cache: QueryCache, +{ + let key = key.into_query_param(); + match try_get_cached(tcx, query_cache, &key) { + Some(value) => value, + None => execute_query(tcx, span, key, QueryMode::Get).unwrap(), + } +} + +/// Shared implementation of `tcx.ensure_ok().$query(..)` for most queries, +/// and `tcx.ensure_done().$query(..)` for all queries. +#[inline] +pub(crate) fn query_ensure<'tcx, Cache>( + tcx: TyCtxt<'tcx>, + execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, + query_cache: &Cache, + key: Cache::Key, + check_cache: bool, +) where + Cache: QueryCache, +{ + let key = key.into_query_param(); + if try_get_cached(tcx, query_cache, &key).is_none() { + execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }); + } +} + +/// Shared implementation of `tcx.ensure_ok().$query(..)` for queries that +/// have the `return_result_from_ensure_ok` modifier. +#[inline] +pub(crate) fn query_ensure_error_guaranteed<'tcx, Cache, T>( + tcx: TyCtxt<'tcx>, + execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, + query_cache: &Cache, + key: Cache::Key, + check_cache: bool, +) -> Result<(), ErrorGuaranteed> +where + Cache: QueryCache>>, + Result: EraseType, +{ + let key = key.into_query_param(); + if let Some(res) = try_get_cached(tcx, query_cache, &key) { + erase::restore(res).map(drop) + } else { + execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }) + .map(erase::restore) + .map(|res| res.map(drop)) + // Either we actually executed the query, which means we got a full `Result`, + // or we can just assume the query succeeded, because it was green in the + // incremental cache. If it is green, that means that the previous compilation + // that wrote to the incremental cache compiles successfully. That is only + // possible if the cache entry was `Ok(())`, so we emit that here, without + // actually encoding the `Result` in the cache or loading it from there. + .unwrap_or(Ok(())) + } +} diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 30c4b4369830d..4bac1e8406c14 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -121,9 +121,7 @@ use crate::mir::interpret::{ }; use crate::mir::mono::{CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions}; use crate::query::erase::{Erase, erase, restore}; -use crate::query::plumbing::{ - CyclePlaceholder, DynamicQuery, query_ensure, query_ensure_error_guaranteed, query_get_at, -}; +use crate::query::plumbing::{CyclePlaceholder, DynamicQuery}; use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, @@ -147,6 +145,7 @@ use crate::{dep_graph, mir, thir}; mod arena_cached; pub mod erase; +pub(crate) mod inner; mod keys; pub mod on_disk_cache; #[macro_use] diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 16cc81dacad4b..c2fc43b444b87 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -8,10 +8,9 @@ use rustc_query_system::HandleCycleError; use rustc_query_system::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; pub(crate) use rustc_query_system::query::QueryJobId; use rustc_query_system::query::*; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, Span}; pub use sealed::IntoQueryParam; -use super::erase::EraseType; use crate::dep_graph; use crate::dep_graph::DepKind; use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; @@ -167,78 +166,17 @@ impl<'tcx> TyCtxt<'tcx> { } } -#[inline(always)] -pub fn query_get_at<'tcx, Cache>( - tcx: TyCtxt<'tcx>, - execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, - query_cache: &Cache, - span: Span, - key: Cache::Key, -) -> Cache::Value -where - Cache: QueryCache, -{ - let key = key.into_query_param(); - match try_get_cached(tcx, query_cache, &key) { - Some(value) => value, - None => execute_query(tcx, span, key, QueryMode::Get).unwrap(), - } -} - -#[inline] -pub fn query_ensure<'tcx, Cache>( - tcx: TyCtxt<'tcx>, - execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, - query_cache: &Cache, - key: Cache::Key, - check_cache: bool, -) where - Cache: QueryCache, -{ - let key = key.into_query_param(); - if try_get_cached(tcx, query_cache, &key).is_none() { - execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }); - } -} - -#[inline] -pub fn query_ensure_error_guaranteed<'tcx, Cache, T>( - tcx: TyCtxt<'tcx>, - execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, - query_cache: &Cache, - key: Cache::Key, - check_cache: bool, -) -> Result<(), ErrorGuaranteed> -where - Cache: QueryCache>>, - Result: EraseType, -{ - let key = key.into_query_param(); - if let Some(res) = try_get_cached(tcx, query_cache, &key) { - super::erase::restore(res).map(drop) - } else { - execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }) - .map(super::erase::restore) - .map(|res| res.map(drop)) - // Either we actually executed the query, which means we got a full `Result`, - // or we can just assume the query succeeded, because it was green in the - // incremental cache. If it is green, that means that the previous compilation - // that wrote to the incremental cache compiles successfully. That is only - // possible if the cache entry was `Ok(())`, so we emit that here, without - // actually encoding the `Result` in the cache or loading it from there. - .unwrap_or(Ok(())) - } -} - -macro_rules! query_ensure { +/// Calls either `query_ensure` or `query_ensure_error_guaranteed`, depending +/// on whether the list of modifiers contains `return_result_from_ensure_ok`. +macro_rules! query_ensure_select { ([]$($args:tt)*) => { - query_ensure($($args)*) + crate::query::inner::query_ensure($($args)*) }; ([(return_result_from_ensure_ok) $($rest:tt)*]$($args:tt)*) => { - query_ensure_error_guaranteed($($args)*).map(|_| ()) + crate::query::inner::query_ensure_error_guaranteed($($args)*) }; ([$other:tt $($modifiers:tt)*]$($args:tt)*) => { - query_ensure!([$($modifiers)*]$($args)*) + query_ensure_select!([$($modifiers)*]$($args)*) }; } @@ -434,7 +372,7 @@ macro_rules! define_callbacks { self, key: query_helper_param_ty!($($K)*), ) -> ensure_ok_result!([$($modifiers)*]) { - query_ensure!( + query_ensure_select!( [$($modifiers)*] self.tcx, self.tcx.query_system.fns.engine.$name, @@ -449,7 +387,7 @@ macro_rules! define_callbacks { $($(#[$attr])* #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) { - query_ensure( + crate::query::inner::query_ensure( self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, @@ -474,7 +412,7 @@ macro_rules! define_callbacks { #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V { - restore::<$V>(query_get_at( + restore::<$V>(crate::query::inner::query_get_at( self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, From cb0c092c786acebe56b915344b82e53d06b0e1d4 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 6 Oct 2025 17:22:39 +1100 Subject: [PATCH 07/12] Prepare `define_feedable!` for code extraction --- compiler/rustc_middle/src/query/mod.rs | 5 +---- compiler/rustc_middle/src/query/plumbing.rs | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 4bac1e8406c14..a81c2530a3928 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -70,7 +70,6 @@ use std::sync::Arc; use rustc_abi::Align; use rustc_arena::TypedArena; use rustc_ast::expand::allocator::AllocatorKind; -use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::steal::Steal; @@ -88,9 +87,7 @@ use rustc_index::IndexVec; use rustc_lint_defs::LintId; use rustc_macros::rustc_queries; use rustc_query_system::ich::StableHashingContext; -use rustc_query_system::query::{ - QueryCache, QueryMode, QueryStackDeferred, QueryState, try_get_cached, -}; +use rustc_query_system::query::{QueryMode, QueryStackDeferred, QueryState}; use rustc_session::Limits; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; use rustc_session::cstore::{ diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index c2fc43b444b87..cb8a15c029f4b 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -501,17 +501,22 @@ macro_rules! define_feedable { $(#[$attr])* #[inline(always)] pub fn $name(self, value: queries::$name::ProvidedValue<'tcx>) { + #![allow(unused_imports)] // Removed later in this PR. + use rustc_data_structures::fingerprint::Fingerprint; + use rustc_query_system::query::{QueryCache, try_get_cached}; + let key = self.key().into_query_param(); let tcx = self.tcx; let erased = queries::$name::provided_to_erased(tcx, value); - let value = restore::<$V>(erased); + let value = erase::restore::<$V>(erased); let cache = &tcx.query_system.caches.$name; + let dep_kind: dep_graph::DepKind = dep_graph::dep_kinds::$name; let hasher: Option, &_) -> _> = hash_result!([$($modifiers)*]); match try_get_cached(tcx, cache, &key) { Some(old) => { - let old = restore::<$V>(old); + let old = erase::restore::<$V>(old); if let Some(hasher) = hasher { let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx.with_stable_hashing_context(|mut hcx| (hasher(&mut hcx, &value), hasher(&mut hcx, &old)) @@ -521,9 +526,8 @@ macro_rules! define_feedable { // results is tainted by errors. In this case, delay a bug to // ensure compilation is doomed, and keep the `old` value. tcx.dcx().delayed_bug(format!( - "Trying to feed an already recorded value for query {} key={key:?}:\n\ + "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ old value: {old:?}\nnew value: {value:?}", - stringify!($name), )); } } else { @@ -531,18 +535,18 @@ macro_rules! define_feedable { // If feeding the same value multiple times needs to be supported, // the query should not be marked `no_hash`. bug!( - "Trying to feed an already recorded value for query {} key={key:?}:\nold value: {old:?}\nnew value: {value:?}", - stringify!($name), + "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ + old value: {old:?}\nnew value: {value:?}", ) } } None => { - let dep_node = dep_graph::DepNode::construct(tcx, dep_graph::dep_kinds::$name, &key); + let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key); let dep_node_index = tcx.dep_graph.with_feed_task( dep_node, tcx, &value, - hash_result!([$($modifiers)*]), + hasher, ); cache.complete(key, erased, dep_node_index); } From a282336c5ca01d1be21ee21f7fb872341a36a65b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 6 Oct 2025 16:07:09 +1100 Subject: [PATCH 08/12] Extract most code from `define_feedable!` --- compiler/rustc_middle/src/query/inner.rs | 56 +++++++++++++++++++++ compiler/rustc_middle/src/query/plumbing.rs | 51 ++++--------------- 2 files changed, 65 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index 6efe21bc89976..ee828ae55f7a3 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -1,9 +1,15 @@ //! Helper functions that serve as the immediate implementation of //! `tcx.$query(..)` and its variations. +use std::fmt::Debug; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_query_system::dep_graph::{DepKind, DepNodeParams}; +use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{QueryCache, QueryMode, try_get_cached}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +use crate::dep_graph; use crate::query::IntoQueryParam; use crate::query::erase::{self, Erase, EraseType}; use crate::ty::TyCtxt; @@ -76,3 +82,53 @@ where .unwrap_or(Ok(())) } } + +/// Common implementation of query feeding, used by `define_feedable!`. +pub(crate) fn query_feed<'tcx, Cache, Value>( + tcx: TyCtxt<'tcx>, + dep_kind: DepKind, + hasher: Option, &Value) -> Fingerprint>, + cache: &Cache, + key: Cache::Key, + erased: Erase, +) where + Cache: QueryCache>, + Cache::Key: DepNodeParams>, + Value: EraseType + Debug, +{ + let value = erase::restore::(erased); + + match try_get_cached(tcx, cache, &key) { + Some(old) => { + let old = erase::restore::(old); + if let Some(hasher) = hasher { + let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx + .with_stable_hashing_context(|mut hcx| { + (hasher(&mut hcx, &value), hasher(&mut hcx, &old)) + }); + if old_hash != value_hash { + // We have an inconsistency. This can happen if one of the two + // results is tainted by errors. In this case, delay a bug to + // ensure compilation is doomed, and keep the `old` value. + tcx.dcx().delayed_bug(format!( + "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ + old value: {old:?}\nnew value: {value:?}", + )); + } + } else { + // The query is `no_hash`, so we have no way to perform a sanity check. + // If feeding the same value multiple times needs to be supported, + // the query should not be marked `no_hash`. + bug!( + "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ + old value: {old:?}\nnew value: {value:?}", + ) + } + } + None => { + let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key); + let dep_node_index = tcx.dep_graph.with_feed_task(dep_node, tcx, &value, hasher); + cache.complete(key, erased, dep_node_index); + } + } +} diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index cb8a15c029f4b..8d01d9482ed4b 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -501,56 +501,23 @@ macro_rules! define_feedable { $(#[$attr])* #[inline(always)] pub fn $name(self, value: queries::$name::ProvidedValue<'tcx>) { - #![allow(unused_imports)] // Removed later in this PR. - use rustc_data_structures::fingerprint::Fingerprint; - use rustc_query_system::query::{QueryCache, try_get_cached}; - let key = self.key().into_query_param(); let tcx = self.tcx; let erased = queries::$name::provided_to_erased(tcx, value); - let value = erase::restore::<$V>(erased); let cache = &tcx.query_system.caches.$name; let dep_kind: dep_graph::DepKind = dep_graph::dep_kinds::$name; let hasher: Option, &_) -> _> = hash_result!([$($modifiers)*]); - match try_get_cached(tcx, cache, &key) { - Some(old) => { - let old = erase::restore::<$V>(old); - if let Some(hasher) = hasher { - let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx.with_stable_hashing_context(|mut hcx| - (hasher(&mut hcx, &value), hasher(&mut hcx, &old)) - ); - if old_hash != value_hash { - // We have an inconsistency. This can happen if one of the two - // results is tainted by errors. In this case, delay a bug to - // ensure compilation is doomed, and keep the `old` value. - tcx.dcx().delayed_bug(format!( - "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ - old value: {old:?}\nnew value: {value:?}", - )); - } - } else { - // The query is `no_hash`, so we have no way to perform a sanity check. - // If feeding the same value multiple times needs to be supported, - // the query should not be marked `no_hash`. - bug!( - "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ - old value: {old:?}\nnew value: {value:?}", - ) - } - } - None => { - let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key); - let dep_node_index = tcx.dep_graph.with_feed_task( - dep_node, - tcx, - &value, - hasher, - ); - cache.complete(key, erased, dep_node_index); - } - } + + $crate::query::inner::query_feed( + tcx, + dep_kind, + hasher, + cache, + key, + erased, + ); } })* } From 0abecda9edebdea76e07092ffb4f6c4261b25228 Mon Sep 17 00:00:00 2001 From: AMS21 Date: Fri, 10 Oct 2025 12:16:11 +0200 Subject: [PATCH 09/12] Replace `LLVMRustContextCreate` with normal LLVM-C API calls Since `LLVMRustContextCreate` can easily be replaced with a call to `LLVMContextCreate` and `LLVMContextSetDiscardValueNames`. --- compiler/rustc_codegen_llvm/src/lib.rs | 11 ++++++++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 5 ++--- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 6 ------ 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 807049f08d367..a2ed11a83b13a 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -48,6 +48,8 @@ use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; use rustc_span::Symbol; use rustc_target::spec::{RelocModel, TlsModel}; +use crate::llvm::ToLlvmBool; + mod abi; mod allocator; mod asm; @@ -384,7 +386,8 @@ unsafe impl Sync for ModuleLlvm {} impl ModuleLlvm { fn new(tcx: TyCtxt<'_>, mod_name: &str) -> Self { unsafe { - let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names()); + let llcx = llvm::LLVMContextCreate(); + llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool()); let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; ModuleLlvm { llmod_raw, @@ -396,7 +399,8 @@ impl ModuleLlvm { fn new_metadata(tcx: TyCtxt<'_>, mod_name: &str) -> Self { unsafe { - let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names()); + let llcx = llvm::LLVMContextCreate(); + llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool()); let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; ModuleLlvm { llmod_raw, @@ -427,7 +431,8 @@ impl ModuleLlvm { dcx: DiagCtxtHandle<'_>, ) -> Self { unsafe { - let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); + let llcx = llvm::LLVMContextCreate(); + llvm::LLVMContextSetDiscardValueNames(llcx, cgcx.fewer_names.to_llvm_bool()); let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx); let tm = ModuleLlvm::tm_from_cgcx(cgcx, name.to_str().unwrap(), dcx); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 67e8bc7062bd9..1740dbd1684d0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -905,7 +905,9 @@ pub(crate) type GetSymbolsErrorCallback = unsafe extern "C" fn(*const c_char) -> unsafe extern "C" { // Create and destroy contexts. + pub(crate) fn LLVMContextCreate() -> &'static mut Context; pub(crate) fn LLVMContextDispose(C: &'static mut Context); + pub(crate) fn LLVMContextSetDiscardValueNames(C: &Context, Discard: Bool); pub(crate) fn LLVMGetMDKindIDInContext( C: &Context, Name: *const c_char, @@ -1925,9 +1927,6 @@ unsafe extern "C" { pub(crate) fn LLVMRustInstallErrorHandlers(); pub(crate) fn LLVMRustDisableSystemDialogsOnCrash(); - // Create and destroy contexts. - pub(crate) fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context; - // Operations on all values pub(crate) fn LLVMRustGlobalAddMetadata<'a>( Val: &'a Value, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 3722f8d689e53..094e018058aad 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -123,12 +123,6 @@ extern "C" void LLVMRustSetLastError(const char *Err) { LastError = strdup(Err); } -extern "C" LLVMContextRef LLVMRustContextCreate(bool shouldDiscardNames) { - auto ctx = new LLVMContext(); - ctx->setDiscardValueNames(shouldDiscardNames); - return wrap(ctx); -} - extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M, const char *Target) { #if LLVM_VERSION_GE(21, 0) From dff4561f591cca31938763b577e81a98e425f6ea Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Fri, 10 Oct 2025 20:01:02 +0100 Subject: [PATCH 10/12] Fix documentation of Instant::now on mac --- library/std/src/time.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 31187adb6feae..87aaf9091f1bc 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -112,19 +112,19 @@ use crate::sys_common::{FromInner, IntoInner}; /// | Platform | System call | /// |-----------|----------------------------------------------------------------------| /// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] | -/// | UNIX | [clock_gettime (Monotonic Clock)] | -/// | Darwin | [clock_gettime (Monotonic Clock)] | -/// | VXWorks | [clock_gettime (Monotonic Clock)] | +/// | UNIX | [clock_gettime] with `CLOCK_MONOTONIC` | +/// | Darwin | [clock_gettime] with `CLOCK_UPTIME_RAW` | +/// | VXWorks | [clock_gettime] with `CLOCK_MONOTONIC` | /// | SOLID | `get_tim` | -/// | WASI | [__wasi_clock_time_get (Monotonic Clock)] | +/// | WASI | [__wasi_clock_time_get] with `monotonic` | /// | Windows | [QueryPerformanceCounter] | /// /// [currently]: crate::io#platform-specific-behavior /// [QueryPerformanceCounter]: https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter /// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time /// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode -/// [__wasi_clock_time_get (Monotonic Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get -/// [clock_gettime (Monotonic Clock)]: https://linux.die.net/man/3/clock_gettime +/// [__wasi_clock_time_get]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get +/// [clock_gettime]: https://linux.die.net/man/3/clock_gettime /// /// **Disclaimer:** These system calls might change over time. /// From 340702c0c2524e57e1a4950c22c3bd1b01e4a586 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Wed, 17 Sep 2025 22:56:47 +0100 Subject: [PATCH 11/12] write x.py's help for saving output time Currently x.py help (or x.py --help) builds bootstrap binary everytime, but it delays printing help. This change saves the current top level help text into a file. x.py help prints the file and doesn't touch bootstrap binary. x.py test bootstrap checks if the file is up to date. Note that subcommand level helps (e.g., x.py check --help) aren't saved. --- src/bootstrap/bootstrap.py | 24 +++++- src/bootstrap/src/core/build_steps/run.rs | 29 ++++++- src/bootstrap/src/core/build_steps/test.rs | 21 ++++- src/bootstrap/src/core/builder/mod.rs | 1 + src/bootstrap/src/core/config/flags.rs | 6 ++ src/etc/xhelp | 96 ++++++++++++++++++++++ 6 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 src/etc/xhelp diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 8b1775178c915..4dd465edb0df9 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1379,11 +1379,26 @@ def main(): sys.argv[1] = "-h" args = parse_args(sys.argv) - help_triggered = args.help or len(sys.argv) == 1 - # If the user is asking for help, let them know that the whole download-and-build + # Root help (e.g., x.py --help) prints help from the saved file to save the time + if len(sys.argv) == 1 or sys.argv[1] in ["-h", "--help"]: + try: + with open( + os.path.join(os.path.dirname(__file__), "../etc/xhelp"), "r" + ) as f: + # The file from bootstrap func already has newline. + print(f.read(), end="") + sys.exit(0) + except Exception as error: + eprint( + f"ERROR: unable to run help: {error}\n", + "x.py run generate-help may solve the problem.", + ) + sys.exit(1) + + # If the user is asking for other helps, let them know that the whole download-and-build # process has to happen before anything is printed out. - if help_triggered: + if args.help: eprint( "INFO: Downloading and building bootstrap before processing --help command.\n" " See src/bootstrap/README.md for help with common commands." @@ -1401,13 +1416,14 @@ def main(): eprint(error) success_word = "unsuccessfully" - if not help_triggered: + if not args.help: eprint( "Build completed", success_word, "in", format_build_time(time() - start_time), ) + sys.exit(exit_code) diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index b9a4c1bf9b44b..bd5ba89229117 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -15,7 +15,7 @@ use crate::core::build_steps::tool::{self, RustcPrivateCompilers, SourceType, To use crate::core::build_steps::vendor::{Vendor, default_paths_to_vendor}; use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata}; use crate::core::config::TargetSelection; -use crate::core::config::flags::get_completion; +use crate::core::config::flags::{get_completion, top_level_help}; use crate::utils::exec::command; use crate::{Mode, t}; @@ -512,3 +512,30 @@ impl Step for Rustfmt { rustfmt.into_cmd().run(builder); } } + +/// Return the path of x.py's help. +pub fn get_help_path(builder: &Builder<'_>) -> PathBuf { + builder.src.join("src/etc/xhelp") +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GenerateHelp; + +impl Step for GenerateHelp { + type Output = (); + + fn run(self, builder: &Builder<'_>) { + let help = top_level_help(); + let path = get_help_path(builder); + std::fs::write(&path, help) + .unwrap_or_else(|e| panic!("writing help into {} failed: {e:?}", path.display())); + } + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("generate-help") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(GenerateHelp) + } +} diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 154b209b20252..4babd32d5d4c4 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -15,7 +15,7 @@ use crate::core::build_steps::compile::{Std, run_cargo}; use crate::core::build_steps::doc::{DocumentationFormat, prepare_doc_compiler}; use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags}; use crate::core::build_steps::llvm::get_llvm_version; -use crate::core::build_steps::run::get_completion_paths; +use crate::core::build_steps::run::{get_completion_paths, get_help_path}; use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, Tool, @@ -28,7 +28,7 @@ use crate::core::builder::{ crate_description, }; use crate::core::config::TargetSelection; -use crate::core::config::flags::{Subcommand, get_completion}; +use crate::core::config::flags::{Subcommand, get_completion, top_level_help}; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ @@ -1292,6 +1292,23 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to ); crate::exit!(1); } + + builder.info("x.py help check"); + if builder.config.cmd.bless() { + builder.ensure(crate::core::build_steps::run::GenerateHelp); + } else { + let help_path = get_help_path(builder); + let cur_help = std::fs::read_to_string(&help_path).unwrap_or_else(|err| { + eprintln!("couldn't read {}: {}", help_path.display(), err); + crate::exit!(1); + }); + let new_help = top_level_help(); + + if new_help != cur_help { + eprintln!("x.py help was changed; run `x.py run generate-help` to update it"); + crate::exit!(1); + } + } } fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 99fb62ea31c9d..d660680942751 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1239,6 +1239,7 @@ impl<'a> Builder<'a> { run::CyclicStep, run::CoverageDump, run::Rustfmt, + run::GenerateHelp, ), Kind::Setup => { describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor) diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index c01b71b926068..9e408505933d3 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -701,3 +701,9 @@ pub fn get_completion(shell: &dyn Generator, path: &Path) -> Option { } Some(String::from_utf8(buf).expect("completion script should be UTF-8")) } + +/// Return the top level help of the bootstrap. +pub fn top_level_help() -> String { + let mut cmd = Flags::command(); + cmd.render_help().to_string() +} diff --git a/src/etc/xhelp b/src/etc/xhelp new file mode 100644 index 0000000000000..5880d070d3d66 --- /dev/null +++ b/src/etc/xhelp @@ -0,0 +1,96 @@ + +Usage: x.py [options] [...] + +Commands: + build Compile either the compiler or libraries + check Compile either the compiler or libraries, using cargo check + clippy Run Clippy (uses rustup/cargo-installed clippy binary) + fix Run cargo fix + fmt Run rustfmt + doc Build documentation + test Build and run some test suites + miri Build and run some test suites *in Miri* + bench Build and run some benchmarks + clean Clean out build directories + dist Build distribution artifacts + install Install distribution artifacts + run Run tools contained in this repository + setup Set up the environment for development + vendor Vendor dependencies + perf Perform profiling and benchmarking of the compiler using `rustc-perf` + +Arguments: + [PATHS]... paths for the subcommand + [ARGS]... arguments passed to subcommands + +Options: + -v, --verbose... + use verbose output (-vv for very verbose) + -i, --incremental + use incremental compilation + --config + TOML configuration file for build + --build-dir + Build directory, overrides `build.build-dir` in `bootstrap.toml` + --build + host target of the stage0 compiler + --host + host targets to build + --target + target targets to build + --exclude + build paths to exclude + --skip + build paths to skip + --include-default-paths + include default paths in addition to the provided ones + --rustc-error-format + rustc error format + --on-fail + command to run on failure + --dry-run + dry run; don't build anything + --dump-bootstrap-shims + Indicates whether to dump the work done from bootstrap shims + --stage + stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.) + --keep-stage + stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1) + --keep-stage-std + stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1) + --src + path to the root of the rust checkout + -j, --jobs + number of jobs to run in parallel + --warnings + if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour [default: default] [possible values: deny, warn, default] + --json-output + use message-format=json + --compile-time-deps + only build proc-macros and build scripts (for rust-analyzer) + --color