From 4a5bcbb4e4fb402719d0d6c4da17a3ca43be66c2 Mon Sep 17 00:00:00 2001 From: chloekek Date: Mon, 15 Apr 2024 00:23:05 +0200 Subject: [PATCH 01/22] Add vec_deque::Iter::as_slices and friends Add the following methods, that work similarly to VecDeque::as_slices: - alloc::collections::vec_deque::Iter::as_slices - alloc::collections::vec_deque::IterMut::into_slices - alloc::collections::vec_deque::IterMut::as_slices - alloc::collections::vec_deque::IterMut::as_mut_slices Obtaining slices from a VecDeque iterator was not previously possible. --- .../alloc/src/collections/vec_deque/iter.rs | 34 ++++++ .../src/collections/vec_deque/iter_mut.rs | 107 ++++++++++++++++++ library/alloc/src/lib.rs | 1 + 3 files changed, 142 insertions(+) diff --git a/library/alloc/src/collections/vec_deque/iter.rs b/library/alloc/src/collections/vec_deque/iter.rs index 5a5e7f70854d8..74eeed3f9e1a8 100644 --- a/library/alloc/src/collections/vec_deque/iter.rs +++ b/library/alloc/src/collections/vec_deque/iter.rs @@ -19,6 +19,40 @@ impl<'a, T> Iter<'a, T> { pub(super) fn new(i1: slice::Iter<'a, T>, i2: slice::Iter<'a, T>) -> Self { Self { i1, i2 } } + + /// Views the underlying data as a pair of subslices of the original data. + /// + /// The slices contain, in order, the contents of the deque not yet yielded + /// by the iterator. + /// + /// This has the same lifetime as the original `VecDeque`, and so the + /// iterator can continue to be used while this exists. + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_deque_iter_as_slices)] + /// + /// use std::collections::VecDeque; + /// + /// let mut deque = VecDeque::new(); + /// deque.push_back(0); + /// deque.push_back(1); + /// deque.push_back(2); + /// deque.push_front(10); + /// deque.push_front(9); + /// deque.push_front(8); + /// + /// let mut iter = deque.iter(); + /// iter.next(); + /// iter.next_back(); + /// + /// assert_eq!(iter.as_slices(), (&[9, 10][..], &[0, 1][..])); + /// ``` + #[unstable(feature = "vec_deque_iter_as_slices", issue = "123947")] + pub fn as_slices(&self) -> (&'a [T], &'a [T]) { + (self.i1.as_slice(), self.i2.as_slice()) + } } #[stable(feature = "collection_debug", since = "1.17.0")] diff --git a/library/alloc/src/collections/vec_deque/iter_mut.rs b/library/alloc/src/collections/vec_deque/iter_mut.rs index 5061931afb7b7..db81f0d1e1d95 100644 --- a/library/alloc/src/collections/vec_deque/iter_mut.rs +++ b/library/alloc/src/collections/vec_deque/iter_mut.rs @@ -19,6 +19,113 @@ impl<'a, T> IterMut<'a, T> { pub(super) fn new(i1: slice::IterMut<'a, T>, i2: slice::IterMut<'a, T>) -> Self { Self { i1, i2 } } + + /// Views the underlying data as a pair of subslices of the original data. + /// + /// The slices contain, in order, the contents of the deque not yet yielded + /// by the iterator. + /// + /// To avoid creating `&mut` references that alias, this is forced to + /// consume the iterator. + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_deque_iter_as_slices)] + /// + /// use std::collections::VecDeque; + /// + /// let mut deque = VecDeque::new(); + /// deque.push_back(0); + /// deque.push_back(1); + /// deque.push_back(2); + /// deque.push_front(10); + /// deque.push_front(9); + /// deque.push_front(8); + /// + /// let mut iter = deque.iter_mut(); + /// iter.next(); + /// iter.next_back(); + /// + /// let slices = iter.into_slices(); + /// slices.0[0] = 42; + /// slices.1[0] = 24; + /// assert_eq!(deque.as_slices(), (&[8, 42, 10][..], &[24, 1, 2][..])); + /// ``` + #[unstable(feature = "vec_deque_iter_as_slices", issue = "123947")] + pub fn into_slices(self) -> (&'a mut [T], &'a mut [T]) { + (self.i1.into_slice(), self.i2.into_slice()) + } + + /// Views the underlying data as a pair of subslices of the original data. + /// + /// The slices contain, in order, the contents of the deque not yet yielded + /// by the iterator. + /// + /// To avoid creating `&mut [T]` references that alias, the returned slices + /// borrow their lifetimes from the iterator the method is applied on. + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_deque_iter_as_slices)] + /// + /// use std::collections::VecDeque; + /// + /// let mut deque = VecDeque::new(); + /// deque.push_back(0); + /// deque.push_back(1); + /// deque.push_back(2); + /// deque.push_front(10); + /// deque.push_front(9); + /// deque.push_front(8); + /// + /// let mut iter = deque.iter_mut(); + /// iter.next(); + /// iter.next_back(); + /// + /// assert_eq!(iter.as_slices(), (&[9, 10][..], &[0, 1][..])); + /// ``` + #[unstable(feature = "vec_deque_iter_as_slices", issue = "123947")] + pub fn as_slices(&self) -> (&[T], &[T]) { + (self.i1.as_slice(), self.i2.as_slice()) + } + + /// Views the underlying data as a pair of subslices of the original data. + /// + /// The slices contain, in order, the contents of the deque not yet yielded + /// by the iterator. + /// + /// To avoid creating `&mut [T]` references that alias, the returned slices + /// borrow their lifetimes from the iterator the method is applied on. + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_deque_iter_as_slices)] + /// + /// use std::collections::VecDeque; + /// + /// let mut deque = VecDeque::new(); + /// deque.push_back(0); + /// deque.push_back(1); + /// deque.push_back(2); + /// deque.push_front(10); + /// deque.push_front(9); + /// deque.push_front(8); + /// + /// let mut iter = deque.iter_mut(); + /// iter.next(); + /// iter.next_back(); + /// + /// iter.as_mut_slices().0[0] = 42; + /// iter.as_mut_slices().1[0] = 24; + /// assert_eq!(deque.as_slices(), (&[8, 42, 10][..], &[24, 1, 2][..])); + /// ``` + #[unstable(feature = "vec_deque_iter_as_slices", issue = "123947")] + pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) { + (self.i1.as_mut_slice(), self.i2.as_mut_slice()) + } } #[stable(feature = "collection_debug", since = "1.17.0")] diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index dec04d7e421e3..9862af7f1e7a8 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -150,6 +150,7 @@ #![feature(sized_type_properties)] #![feature(slice_from_ptr_range)] #![feature(slice_index_methods)] +#![feature(slice_iter_mut_as_mut_slice)] #![feature(slice_ptr_get)] #![feature(slice_range)] #![feature(std_internals)] From 37ea2028beed52aa8cc2406b75f59a98d02ed457 Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:12:40 +0100 Subject: [PATCH 02/22] ci: use free runner in dist-i686-msvc --- src/ci/github-actions/jobs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 340dfd67b7dd7..80ae62d7ac01d 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -465,7 +465,7 @@ auto: SCRIPT: python x.py dist bootstrap --include-default-paths DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift - <<: *job-windows-8c + <<: *job-windows - image: dist-aarch64-msvc env: From 09c268417f596520de48dea387bdbf12a08da29c Mon Sep 17 00:00:00 2001 From: maxcabrajac Date: Mon, 18 Nov 2024 15:40:34 -0300 Subject: [PATCH 03/22] Add Visitor::visit_fn_decl --- compiler/rustc_ast/src/visit.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 3500c2153765d..03fd3c279500f 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -299,6 +299,9 @@ pub trait Visitor<'ast>: Sized { fn visit_coroutine_kind(&mut self, _coroutine_kind: &'ast CoroutineKind) -> Self::Result { Self::Result::output() } + fn visit_fn_decl(&mut self, fn_decl: &'ast FnDecl) -> Self::Result { + walk_fn_decl(self, fn_decl) + } } pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) -> V::Result { @@ -518,7 +521,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result { let BareFnTy { safety: _, ext: _, generic_params, decl, decl_span: _ } = &**function_declaration; walk_list!(visitor, visit_generic_param, generic_params); - try_visit!(walk_fn_decl(visitor, decl)); + try_visit!(visitor.visit_fn_decl(decl)); } TyKind::Path(maybe_qself, path) => { try_visit!(walk_qself(visitor, maybe_qself)); @@ -846,13 +849,13 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu // Identifier and visibility are visited as a part of the item. try_visit!(visitor.visit_fn_header(header)); try_visit!(visitor.visit_generics(generics)); - try_visit!(walk_fn_decl(visitor, decl)); + try_visit!(visitor.visit_fn_decl(decl)); visit_opt!(visitor, visit_block, body); } FnKind::Closure(binder, coroutine_kind, decl, body) => { try_visit!(visitor.visit_closure_binder(binder)); visit_opt!(visitor, visit_coroutine_kind, coroutine_kind.as_ref()); - try_visit!(walk_fn_decl(visitor, decl)); + try_visit!(visitor.visit_fn_decl(decl)); try_visit!(visitor.visit_expr(body)); } } From e65deb5ee1f0273ef357f181c843293a752f29bd Mon Sep 17 00:00:00 2001 From: maxcabrajac Date: Mon, 18 Nov 2024 15:43:35 -0300 Subject: [PATCH 04/22] Add Visitor::visit_qself --- compiler/rustc_ast/src/visit.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 03fd3c279500f..c121e7711ee01 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -302,6 +302,9 @@ pub trait Visitor<'ast>: Sized { fn visit_fn_decl(&mut self, fn_decl: &'ast FnDecl) -> Self::Result { walk_fn_decl(self, fn_decl) } + fn visit_qself(&mut self, qs: &'ast Option>) -> Self::Result { + walk_qself(self, qs) + } } pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) -> V::Result { @@ -437,13 +440,13 @@ impl WalkItemKind for ItemKind { body, from_glob: _, }) => { - try_visit!(walk_qself(visitor, qself)); + try_visit!(visitor.visit_qself(qself)); try_visit!(visitor.visit_path(path, *id)); visit_opt!(visitor, visit_ident, rename); visit_opt!(visitor, visit_block, body); } ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { - try_visit!(walk_qself(visitor, qself)); + try_visit!(visitor.visit_qself(qself)); try_visit!(visitor.visit_path(prefix, id)); if let Some(suffixes) = suffixes { for (ident, rename) in suffixes { @@ -524,7 +527,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result { try_visit!(visitor.visit_fn_decl(decl)); } TyKind::Path(maybe_qself, path) => { - try_visit!(walk_qself(visitor, maybe_qself)); + try_visit!(visitor.visit_qself(maybe_qself)); try_visit!(visitor.visit_path(path, *id)); } TyKind::Pat(ty, pat) => { @@ -655,16 +658,16 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res let Pat { id, kind, span: _, tokens: _ } = pattern; match kind { PatKind::TupleStruct(opt_qself, path, elems) => { - try_visit!(walk_qself(visitor, opt_qself)); + try_visit!(visitor.visit_qself(opt_qself)); try_visit!(visitor.visit_path(path, *id)); walk_list!(visitor, visit_pat, elems); } PatKind::Path(opt_qself, path) => { - try_visit!(walk_qself(visitor, opt_qself)); + try_visit!(visitor.visit_qself(opt_qself)); try_visit!(visitor.visit_path(path, *id)) } PatKind::Struct(opt_qself, path, fields, _rest) => { - try_visit!(walk_qself(visitor, opt_qself)); + try_visit!(visitor.visit_qself(opt_qself)); try_visit!(visitor.visit_path(path, *id)); walk_list!(visitor, visit_pat_field, fields); } @@ -905,13 +908,13 @@ impl WalkItemKind for AssocItemKind { body, from_glob: _, }) => { - try_visit!(walk_qself(visitor, qself)); + try_visit!(visitor.visit_qself(qself)); try_visit!(visitor.visit_path(path, *id)); visit_opt!(visitor, visit_ident, rename); visit_opt!(visitor, visit_block, body); } AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { - try_visit!(walk_qself(visitor, qself)); + try_visit!(visitor.visit_qself(qself)); try_visit!(visitor.visit_path(prefix, id)); if let Some(suffixes) = suffixes { for (ident, rename) in suffixes { @@ -1026,7 +1029,7 @@ pub fn walk_inline_asm_sym<'a, V: Visitor<'a>>( visitor: &mut V, InlineAsmSym { id, qself, path }: &'a InlineAsmSym, ) -> V::Result { - try_visit!(walk_qself(visitor, qself)); + try_visit!(visitor.visit_qself(qself)); visitor.visit_path(path, *id) } @@ -1058,7 +1061,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V } ExprKind::Struct(se) => { let StructExpr { qself, path, fields, rest } = &**se; - try_visit!(walk_qself(visitor, qself)); + try_visit!(visitor.visit_qself(qself)); try_visit!(visitor.visit_path(path, *id)); walk_list!(visitor, visit_expr_field, fields); match rest { @@ -1167,7 +1170,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V } ExprKind::Underscore => {} ExprKind::Path(maybe_qself, path) => { - try_visit!(walk_qself(visitor, maybe_qself)); + try_visit!(visitor.visit_qself(maybe_qself)); try_visit!(visitor.visit_path(path, *id)); } ExprKind::Break(opt_label, opt_expr) => { From f6340f13bb05b0606675e59f10e8c5ec7f2cd7be Mon Sep 17 00:00:00 2001 From: maxcabrajac Date: Mon, 18 Nov 2024 15:49:09 -0300 Subject: [PATCH 05/22] Add MutVisitor::visit_fn_ret_ty --- compiler/rustc_ast/src/mut_visit.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index a09aa9ee66516..811cb0be9f91d 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -330,6 +330,10 @@ pub trait MutVisitor: Sized { fn visit_capture_by(&mut self, capture_by: &mut CaptureBy) { walk_capture_by(self, capture_by) } + + fn visit_fn_ret_ty(&mut self, fn_ret_ty: &mut FnRetTy) { + walk_fn_ret_ty(self, fn_ret_ty) + } } /// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful @@ -609,7 +613,7 @@ fn walk_angle_bracketed_parameter_data(vis: &mut T, data: &mut An fn walk_parenthesized_parameter_data(vis: &mut T, args: &mut ParenthesizedArgs) { let ParenthesizedArgs { inputs, output, span, inputs_span } = args; visit_thin_vec(inputs, |input| vis.visit_ty(input)); - walk_fn_ret_ty(vis, output); + vis.visit_fn_ret_ty(output); vis.visit_span(span); vis.visit_span(inputs_span); } @@ -911,7 +915,7 @@ fn walk_fn(vis: &mut T, kind: FnKind<'_>) { fn walk_fn_decl(vis: &mut T, decl: &mut P) { let FnDecl { inputs, output } = decl.deref_mut(); inputs.flat_map_in_place(|param| vis.flat_map_param(param)); - walk_fn_ret_ty(vis, output); + vis.visit_fn_ret_ty(output); } fn walk_fn_ret_ty(vis: &mut T, fn_ret_ty: &mut FnRetTy) { From 7bffa310426ebcc01cfb1756ae1883118a32366b Mon Sep 17 00:00:00 2001 From: "Panagiotis \"Ivory\" Vasilopoulos" Date: Mon, 18 Nov 2024 18:42:00 +0100 Subject: [PATCH 06/22] Mention std::fs::remove_dir_all in std::fs::remove_dir --- library/std/src/fs.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 76fdb1a4ffd30..555e8804eab33 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2738,6 +2738,10 @@ pub fn create_dir_all>(path: P) -> io::Result<()> { /// Removes an empty directory. /// +/// If you want to remove a directory that is not empty, as well as all +/// of its contents recursively, consider using [`remove_dir_all`] +/// instead. +/// /// # Platform-specific behavior /// /// This function currently corresponds to the `rmdir` function on Unix From df29f9b0c3a2277c2eb2de2e0bf4f4b3329dbd00 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 19 Nov 2024 14:48:48 +1100 Subject: [PATCH 07/22] Improve `fake_ident_or_unknown_prefix`. - Rename it as `invalid_ident_or_prefix`, which matches the possible outputs (`InvalidIdent` or `InvalidPrefix`). - Use the local wrapper for `is_xid_continue`, for consistency. - Make it clear what `\u{200d}` means. --- compiler/rustc_lexer/src/lib.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index f9f2a14dbd275..7f221098364f8 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -468,7 +468,7 @@ impl Cursor<'_> { Literal { kind, suffix_start } } // Identifier starting with an emoji. Only lexed for graceful error recovery. - c if !c.is_ascii() && c.is_emoji_char() => self.fake_ident_or_unknown_prefix(), + c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident_or_prefix(), _ => Unknown, }; let res = Token::new(token_kind, self.pos_within_token()); @@ -552,17 +552,16 @@ impl Cursor<'_> { // we see a prefix here, it is definitely an unknown prefix. match self.first() { '#' | '"' | '\'' => UnknownPrefix, - c if !c.is_ascii() && c.is_emoji_char() => self.fake_ident_or_unknown_prefix(), + c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident_or_prefix(), _ => Ident, } } - fn fake_ident_or_unknown_prefix(&mut self) -> TokenKind { + fn invalid_ident_or_prefix(&mut self) -> TokenKind { // Start is already eaten, eat the rest of identifier. self.eat_while(|c| { - unicode_xid::UnicodeXID::is_xid_continue(c) - || (!c.is_ascii() && c.is_emoji_char()) - || c == '\u{200d}' + const ZERO_WIDTH_JOINER: char = '\u{200d}'; + is_id_continue(c) || (!c.is_ascii() && c.is_emoji_char()) || c == ZERO_WIDTH_JOINER }); // Known prefixes must have been handled earlier. So if // we see a prefix here, it is definitely an unknown prefix. From 2c7c3697dbaac3e0599aa0e7cd3581822caec17b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 19 Nov 2024 15:11:18 +1100 Subject: [PATCH 08/22] Improve `TokenKind` comments. - Improve wording. - Use backticks consistently for examples. --- compiler/rustc_lexer/src/lib.rs | 80 ++++++++++++++++----------------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 7f221098364f8..c01dad810c4f1 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -57,11 +57,10 @@ impl Token { /// Enum representing common lexeme types. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum TokenKind { - // Multi-char tokens: - /// "// comment" + /// A line comment, e.g. `// comment`. LineComment { doc_style: Option }, - /// `/* block comment */` + /// A block comment, e.g. `/* block comment */`. /// /// Block comments can be recursive, so a sequence like `/* /* */` /// will not be considered terminated and will result in a parsing error. @@ -70,18 +69,17 @@ pub enum TokenKind { /// Any whitespace character sequence. Whitespace, - /// "ident" or "continue" - /// - /// At this step, keywords are also considered identifiers. + /// An identifier or keyword, e.g. `ident` or `continue`. Ident, - /// Like the above, but containing invalid unicode codepoints. + /// An identifier that is invalid because it contains emoji. InvalidIdent, - /// "r#ident" + /// A raw identifier, e.g. "r#ident". RawIdent, - /// An unknown prefix, like `foo#`, `foo'`, `foo"`. + /// An unknown literal prefix, like `foo#`, `foo'`, `foo"`. Excludes + /// literal prefixes that contain emoji, which are considered "invalid". /// /// Note that only the /// prefix (`foo`) is included in the token, not the separator (which is @@ -93,11 +91,12 @@ pub enum TokenKind { /// An unknown prefix in a lifetime, like `'foo#`. /// - /// Note that like above, only the `'` and prefix are included in the token + /// Like `UnknownPrefix`, only the `'` and prefix are included in the token /// and not the separator. UnknownPrefixLifetime, - /// `'r#lt`, which in edition < 2021 is split into several tokens: `'r # lt`. + /// A raw lifetime, e.g. `'r#foo`. In edition < 2021 it will be split into + /// several tokens: `'r` and `#` and `foo`. RawLifetime, /// Similar to the above, but *always* an error on every edition. This is used @@ -110,70 +109,69 @@ pub enum TokenKind { /// Split into the component tokens on older editions. GuardedStrPrefix, - /// Examples: `12u8`, `1.0e-40`, `b"123"`. Note that `_` is an invalid + /// Literals, e.g. `12u8`, `1.0e-40`, `b"123"`. Note that `_` is an invalid /// suffix, but may be present here on string and float literals. Users of /// this type will need to check for and reject that case. /// /// See [LiteralKind] for more details. Literal { kind: LiteralKind, suffix_start: u32 }, - /// "'a" + /// A lifetime, e.g. `'a`. Lifetime { starts_with_number: bool }, - // One-char tokens: - /// ";" + /// `;` Semi, - /// "," + /// `,` Comma, - /// "." + /// `.` Dot, - /// "(" + /// `(` OpenParen, - /// ")" + /// `)` CloseParen, - /// "{" + /// `{` OpenBrace, - /// "}" + /// `}` CloseBrace, - /// "[" + /// `[` OpenBracket, - /// "]" + /// `]` CloseBracket, - /// "@" + /// `@` At, - /// "#" + /// `#` Pound, - /// "~" + /// `~` Tilde, - /// "?" + /// `?` Question, - /// ":" + /// `:` Colon, - /// "$" + /// `$` Dollar, - /// "=" + /// `=` Eq, - /// "!" + /// `!` Bang, - /// "<" + /// `<` Lt, - /// ">" + /// `>` Gt, - /// "-" + /// `-` Minus, - /// "&" + /// `&` And, - /// "|" + /// `|` Or, - /// "+" + /// `+` Plus, - /// "*" + /// `*` Star, - /// "/" + /// `/` Slash, - /// "^" + /// `^` Caret, - /// "%" + /// `%` Percent, /// Unknown token, not expected by the lexer, e.g. "№" From e9a0c3c98c5640070e15a3cb38860a7268c1dca2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 19 Nov 2024 15:55:34 +1100 Subject: [PATCH 09/22] Remove `TokenKind::InvalidPrefix`. It was added in #123752 to handle some cases involving emoji, but it isn't necessary because it's always treated the same as `TokenKind::InvalidIdent`. This commit removes it, which makes things a little simpler. --- compiler/rustc_lexer/src/lib.rs | 21 +++++++------------ compiler/rustc_parse/src/lexer/mod.rs | 5 ++--- src/librustdoc/html/highlight.rs | 7 +++---- .../crates/parser/src/lexed_str.rs | 2 +- 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index c01dad810c4f1..bcb103957badf 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -99,10 +99,6 @@ pub enum TokenKind { /// several tokens: `'r` and `#` and `foo`. RawLifetime, - /// Similar to the above, but *always* an error on every edition. This is used - /// for emoji identifier recovery, as those are not meant to be ever accepted. - InvalidPrefix, - /// Guarded string literal prefix: `#"` or `##`. /// /// Used for reserving "guarded strings" (RFC 3598) in edition 2024. @@ -466,7 +462,7 @@ impl Cursor<'_> { Literal { kind, suffix_start } } // Identifier starting with an emoji. Only lexed for graceful error recovery. - c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident_or_prefix(), + c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident(), _ => Unknown, }; let res = Token::new(token_kind, self.pos_within_token()); @@ -550,23 +546,22 @@ impl Cursor<'_> { // we see a prefix here, it is definitely an unknown prefix. match self.first() { '#' | '"' | '\'' => UnknownPrefix, - c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident_or_prefix(), + c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident(), _ => Ident, } } - fn invalid_ident_or_prefix(&mut self) -> TokenKind { + fn invalid_ident(&mut self) -> TokenKind { // Start is already eaten, eat the rest of identifier. self.eat_while(|c| { const ZERO_WIDTH_JOINER: char = '\u{200d}'; is_id_continue(c) || (!c.is_ascii() && c.is_emoji_char()) || c == ZERO_WIDTH_JOINER }); - // Known prefixes must have been handled earlier. So if - // we see a prefix here, it is definitely an unknown prefix. - match self.first() { - '#' | '"' | '\'' => InvalidPrefix, - _ => InvalidIdent, - } + // An invalid identifier followed by '#' or '"' or '\'' could be + // interpreted as an invalid literal prefix. We don't bother doing that + // because the treatment of invalid identifiers and invalid prefixes + // would be the same. + InvalidIdent } fn c_or_byte_string( diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 226de65445c8a..5023e83bd67c7 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -213,7 +213,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let ident = Symbol::intern(lifetime_name); token::Lifetime(ident, IdentIsRaw::No) } - rustc_lexer::TokenKind::InvalidIdent | rustc_lexer::TokenKind::InvalidPrefix + rustc_lexer::TokenKind::InvalidIdent // Do not recover an identifier with emoji if the codepoint is a confusable // with a recoverable substitution token, like `➖`. if !UNICODE_ARRAY.iter().any(|&(c, _, _)| { @@ -359,8 +359,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent), rustc_lexer::TokenKind::Unknown - | rustc_lexer::TokenKind::InvalidIdent - | rustc_lexer::TokenKind::InvalidPrefix => { + | rustc_lexer::TokenKind::InvalidIdent => { // Don't emit diagnostics for sequences of the same invalid token if swallow_next_invalid > 0 { swallow_next_invalid -= 1; diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 4def80764ea78..29f6f92a6b28e 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -861,10 +861,9 @@ impl<'src> Classifier<'src> { }, Some(c) => c, }, - TokenKind::RawIdent - | TokenKind::UnknownPrefix - | TokenKind::InvalidPrefix - | TokenKind::InvalidIdent => Class::Ident(self.new_span(before, text)), + TokenKind::RawIdent | TokenKind::UnknownPrefix | TokenKind::InvalidIdent => { + Class::Ident(self.new_span(before, text)) + } TokenKind::Lifetime { .. } | TokenKind::RawLifetime | TokenKind::UnknownPrefixLifetime => Class::Lifetime, diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index 3c0eb1b42a60b..c97596d5097ec 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -183,7 +183,7 @@ impl<'a> Converter<'a> { rustc_lexer::TokenKind::Ident => { SyntaxKind::from_keyword(token_text, self.edition).unwrap_or(IDENT) } - rustc_lexer::TokenKind::InvalidPrefix | rustc_lexer::TokenKind::InvalidIdent => { + rustc_lexer::TokenKind::InvalidIdent => { err = "Ident contains invalid characters"; IDENT } From 38f0c099b2e684ea689633eb424d8737a1063a5e Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Tue, 19 Nov 2024 18:48:02 +0800 Subject: [PATCH 10/22] Default-enable `llvm_tools_enabled` when no `config.toml` is present --- src/bootstrap/src/core/config/config.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 95b1303fa71a7..e706aba977b6e 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1255,6 +1255,10 @@ impl Config { }, out: PathBuf::from("build"), + // This is needed by codegen_ssa on macOS to ship `llvm-objcopy` aliased to + // `rust-objcopy` to workaround bad `strip`s on macOS. + llvm_tools_enabled: true, + ..Default::default() } } From fcfb782a7582fc5f2264c2bcb67e6ffe6ea4a0e2 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Tue, 19 Nov 2024 19:04:45 +0800 Subject: [PATCH 11/22] Register change info --- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 1d05f94e3be25..7f62ffb20db80 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -300,4 +300,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "`download-rustc='if-unchanged'` is now a default option for library profile.", }, + ChangeInfo { + change_id: 133207, + severity: ChangeSeverity::Info, + summary: "`rust.llvm-tools` is now enabled by default when no `config.toml` is provided.", + }, ]; From 4b5c88301bcc20de9fa58b7390d78d596f7d7223 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Tue, 19 Nov 2024 20:00:58 +0800 Subject: [PATCH 12/22] Explicitly disable llvm tools for cranelift --- compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index 2f13b0b9cb834..5b3f2a912072b 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -38,6 +38,11 @@ local-rebuild = true codegen-backends = ["cranelift"] deny-warnings = false verbose-tests = false +# The cg_clif sysroot doesn't contain llvm tools and unless llvm_tools is +# disabled bootstrap will crash trying to copy llvm tools for the bootstrap +# compiler. +llvm_tools = false + EOF popd From 616013fc49bc6cb7a627c64114bfdd9784ae6d0c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 19 Nov 2024 07:56:34 -0800 Subject: [PATCH 13/22] Correct the tier listing of `wasm32-wasip2` This target is tier 2, not tier 3, and I forgot to update this. Closes #133206 --- src/doc/rustc/src/platform-support/wasm32-wasip2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip2.md b/src/doc/rustc/src/platform-support/wasm32-wasip2.md index bb2348b201e39..40049ecfa5ff7 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip2.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip2.md @@ -1,6 +1,6 @@ # `wasm32-wasip2` -**Tier: 3** +**Tier: 2** The `wasm32-wasip2` target is a new and still (as of January 2024) an experimental target. This target is an extension to `wasm32-wasip1` target, From 7ac4c04731ecb6eedd63a1f899a0c6207679cbf9 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 22 May 2024 14:10:52 +0200 Subject: [PATCH 14/22] Add std::thread::add_spawn_hook. --- library/std/src/thread/mod.rs | 11 ++++ library/std/src/thread/spawnhook.rs | 92 +++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 library/std/src/thread/spawnhook.rs diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 227ee9d64f375..ee8d688398d2c 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -188,6 +188,11 @@ mod current; pub use current::current; pub(crate) use current::{current_id, drop_current, set_current, try_current}; +mod spawnhook; + +#[unstable(feature = "thread_spawn_hook", issue = "none")] +pub use spawnhook::add_spawn_hook; + //////////////////////////////////////////////////////////////////////////////// // Thread-local storage //////////////////////////////////////////////////////////////////////////////// @@ -485,6 +490,9 @@ impl Builder { Some(name) => Thread::new(id, name.into()), None => Thread::new_unnamed(id), }; + + let hooks = spawnhook::run_spawn_hooks(&my_thread)?; + let their_thread = my_thread.clone(); let my_packet: Arc> = Arc::new(Packet { @@ -535,6 +543,9 @@ impl Builder { } crate::io::set_output_capture(output_capture); + for hook in hooks { + hook(); + } let f = f.into_inner(); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs new file mode 100644 index 0000000000000..c64aea4262b8c --- /dev/null +++ b/library/std/src/thread/spawnhook.rs @@ -0,0 +1,92 @@ +use crate::io; +use crate::sync::RwLock; +use crate::thread::Thread; + +static SPAWN_HOOKS: RwLock< + Vec<&'static (dyn Fn(&Thread) -> io::Result> + Sync)>, +> = RwLock::new(Vec::new()); + +/// Registers a function to run for every new thread spawned. +/// +/// The hook is executed in the parent thread, and returns a function +/// that will be executed in the new thread. +/// +/// The hook is called with the `Thread` handle for the new thread. +/// +/// If the hook returns an `Err`, thread spawning is aborted. In that case, the +/// function used to spawn the thread (e.g. `std::thread::spawn`) will return +/// the error returned by the hook. +/// +/// Hooks can only be added, not removed. +/// +/// The hooks will run in order, starting with the most recently added. +/// +/// # Usage +/// +/// ``` +/// #![feature(thread_spawn_hook)] +/// +/// std::thread::add_spawn_hook(|_| { +/// ..; // This will run in the parent (spawning) thread. +/// Ok(move || { +/// ..; // This will run it the child (spawned) thread. +/// }) +/// }); +/// ``` +/// +/// # Example +/// +/// A spawn hook can be used to initialize thread locals from the parent thread: +/// +/// ``` +/// #![feature(thread_spawn_hook)] +/// +/// use std::cell::Cell; +/// +/// thread_local! { +/// static X: Cell = Cell::new(0); +/// } +/// +/// std::thread::add_spawn_hook(|_| { +/// // Get the value of X in the spawning thread. +/// let value = X.get(); +/// // Set the value of X in the newly spawned thread. +/// Ok(move || { +/// X.set(value); +/// }) +/// }); +/// +/// X.set(123); +/// +/// std::thread::spawn(|| { +/// assert_eq!(X.get(), 123); +/// }).join().unwrap(); +/// ``` +#[unstable(feature = "thread_spawn_hook", issue = "none")] +pub fn add_spawn_hook(hook: F) +where + F: 'static + Sync + Fn(&Thread) -> io::Result, + G: 'static + Send + FnOnce(), +{ + SPAWN_HOOKS.write().unwrap_or_else(|e| e.into_inner()).push(Box::leak(Box::new( + move |thread: &Thread| -> io::Result<_> { + let f: Box = Box::new(hook(thread)?); + Ok(f) + }, + ))); +} + +/// Runs all the spawn hooks. +/// +/// Called on the parent thread. +/// +/// Returns the functions to be called on the newly spawned thread. +pub(super) fn run_spawn_hooks(thread: &Thread) -> io::Result>> { + SPAWN_HOOKS + .read() + .unwrap_or_else(|e| e.into_inner()) + .iter() + .rev() + .map(|hook| hook(thread)) + .collect() +} From ef9055f3eef409e99e1e786f6a90a1684fde810d Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 22 May 2024 14:16:33 +0200 Subject: [PATCH 15/22] Use add_spawn_hook for libtest's output capturing. --- library/std/src/thread/mod.rs | 4 ---- library/test/src/lib.rs | 11 +++++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index ee8d688398d2c..c6b1e0de84cf5 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -502,9 +502,6 @@ impl Builder { }); let their_packet = my_packet.clone(); - let output_capture = crate::io::set_output_capture(None); - crate::io::set_output_capture(output_capture.clone()); - // Pass `f` in `MaybeUninit` because actually that closure might *run longer than the lifetime of `F`*. // See for more details. // To prevent leaks we use a wrapper that drops its contents. @@ -542,7 +539,6 @@ impl Builder { imp::Thread::set_name(name); } - crate::io::set_output_capture(output_capture); for hook in hooks { hook(); } diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 30ccfe2af8dbd..00a0b55c6a9f9 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -24,6 +24,7 @@ #![feature(process_exitcode_internals)] #![feature(panic_can_unwind)] #![feature(test)] +#![feature(thread_spawn_hook)] #![allow(internal_features)] #![warn(rustdoc::unescaped_backticks)] @@ -134,6 +135,16 @@ pub fn test_main(args: &[String], tests: Vec, options: Option Date: Thu, 24 Oct 2024 10:52:47 +0200 Subject: [PATCH 16/22] Update thread spawn hooks. 1. Make the effect thread local. 2. Don't return a io::Result from hooks. --- library/std/src/thread/mod.rs | 7 +- library/std/src/thread/spawnhook.rs | 112 ++++++++++++++++++++-------- library/test/src/lib.rs | 4 +- 3 files changed, 86 insertions(+), 37 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index c6b1e0de84cf5..79621e7fa6bd4 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -491,7 +491,7 @@ impl Builder { None => Thread::new_unnamed(id), }; - let hooks = spawnhook::run_spawn_hooks(&my_thread)?; + let hooks = spawnhook::run_spawn_hooks(&my_thread); let their_thread = my_thread.clone(); @@ -539,12 +539,9 @@ impl Builder { imp::Thread::set_name(name); } - for hook in hooks { - hook(); - } - let f = f.into_inner(); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { + crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.run()); crate::sys::backtrace::__rust_begin_short_backtrace(f) })); // SAFETY: `their_packet` as been built just above and moved by the diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index c64aea4262b8c..9c4e9eb6aa13a 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -1,21 +1,43 @@ -use crate::io; -use crate::sync::RwLock; +use crate::cell::Cell; +use crate::sync::Arc; use crate::thread::Thread; -static SPAWN_HOOKS: RwLock< - Vec<&'static (dyn Fn(&Thread) -> io::Result> + Sync)>, -> = RwLock::new(Vec::new()); +// A thread local linked list of spawn hooks. +crate::thread_local! { + static SPAWN_HOOKS: Cell = const { Cell::new(SpawnHooks { first: None }) }; +} + +#[derive(Default, Clone)] +struct SpawnHooks { + first: Option>, +} + +// Manually implement drop to prevent deep recursion when dropping linked Arc list. +impl Drop for SpawnHooks { + fn drop(&mut self) { + let mut next = self.first.take(); + while let Some(SpawnHook { hook, next: n }) = next.and_then(|n| Arc::into_inner(n)) { + drop(hook); + next = n; + } + } +} + +struct SpawnHook { + hook: Box Box>, + next: Option>, +} -/// Registers a function to run for every new thread spawned. +/// Registers a function to run for every newly thread spawned. /// /// The hook is executed in the parent thread, and returns a function /// that will be executed in the new thread. /// /// The hook is called with the `Thread` handle for the new thread. /// -/// If the hook returns an `Err`, thread spawning is aborted. In that case, the -/// function used to spawn the thread (e.g. `std::thread::spawn`) will return -/// the error returned by the hook. +/// The hook will only be added for the current thread and is inherited by the threads it spawns. +/// In other words, adding a hook has no effect on already running threads (other than the current +/// thread) and the threads they might spawn in the future. /// /// Hooks can only be added, not removed. /// @@ -28,15 +50,15 @@ static SPAWN_HOOKS: RwLock< /// /// std::thread::add_spawn_hook(|_| { /// ..; // This will run in the parent (spawning) thread. -/// Ok(move || { +/// move || { /// ..; // This will run it the child (spawned) thread. -/// }) +/// } /// }); /// ``` /// /// # Example /// -/// A spawn hook can be used to initialize thread locals from the parent thread: +/// A spawn hook can be used to "inherit" a thread local from the parent thread: /// /// ``` /// #![feature(thread_spawn_hook)] @@ -47,13 +69,12 @@ static SPAWN_HOOKS: RwLock< /// static X: Cell = Cell::new(0); /// } /// +/// // This needs to be done once in the main thread before spawning any threads. /// std::thread::add_spawn_hook(|_| { /// // Get the value of X in the spawning thread. /// let value = X.get(); /// // Set the value of X in the newly spawned thread. -/// Ok(move || { -/// X.set(value); -/// }) +/// move || X.set(value) /// }); /// /// X.set(123); @@ -65,15 +86,17 @@ static SPAWN_HOOKS: RwLock< #[unstable(feature = "thread_spawn_hook", issue = "none")] pub fn add_spawn_hook(hook: F) where - F: 'static + Sync + Fn(&Thread) -> io::Result, + F: 'static + Sync + Fn(&Thread) -> G, G: 'static + Send + FnOnce(), { - SPAWN_HOOKS.write().unwrap_or_else(|e| e.into_inner()).push(Box::leak(Box::new( - move |thread: &Thread| -> io::Result<_> { - let f: Box = Box::new(hook(thread)?); - Ok(f) - }, - ))); + SPAWN_HOOKS.with(|h| { + let mut hooks = h.take(); + hooks.first = Some(Arc::new(SpawnHook { + hook: Box::new(move |thread| Box::new(hook(thread))), + next: hooks.first.take(), + })); + h.set(hooks); + }); } /// Runs all the spawn hooks. @@ -81,12 +104,41 @@ where /// Called on the parent thread. /// /// Returns the functions to be called on the newly spawned thread. -pub(super) fn run_spawn_hooks(thread: &Thread) -> io::Result>> { - SPAWN_HOOKS - .read() - .unwrap_or_else(|e| e.into_inner()) - .iter() - .rev() - .map(|hook| hook(thread)) - .collect() +pub(super) fn run_spawn_hooks(thread: &Thread) -> SpawnHookResults { + // Get a snapshot of the spawn hooks. + // (Increments the refcount to the first node.) + let hooks = SPAWN_HOOKS.with(|hooks| { + let snapshot = hooks.take(); + hooks.set(snapshot.clone()); + snapshot + }); + // Iterate over the hooks, run them, and collect the results in a vector. + let mut next: &Option> = &hooks.first; + let mut to_run = Vec::new(); + while let Some(hook) = next { + to_run.push((hook.hook)(thread)); + next = &hook.next; + } + // Pass on the snapshot of the hooks and the results to the new thread, + // which will then run SpawnHookResults::run(). + SpawnHookResults { hooks, to_run } +} + +/// The results of running the spawn hooks. +/// +/// This struct is sent to the new thread. +/// It contains the inherited hooks and the closures to be run. +pub(super) struct SpawnHookResults { + hooks: SpawnHooks, + to_run: Vec>, +} + +impl SpawnHookResults { + // This is run on the newly spawned thread, directly at the start. + pub(super) fn run(self) { + SPAWN_HOOKS.set(self.hooks); + for run in self.to_run { + run(); + } + } } diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 00a0b55c6a9f9..47407df909bdf 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -141,9 +141,9 @@ pub fn test_main(args: &[String], tests: Vec, options: Option Date: Thu, 24 Oct 2024 11:19:02 +0200 Subject: [PATCH 17/22] Add thread Builder::no_hooks(). --- library/std/src/thread/mod.rs | 22 +++++++++++++++++++--- library/std/src/thread/spawnhook.rs | 9 +++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 79621e7fa6bd4..4a4c2c4b96020 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -264,6 +264,8 @@ pub struct Builder { name: Option, // The size of the stack for the spawned thread in bytes stack_size: Option, + // Skip running and inheriting the thread spawn hooks + no_hooks: bool, } impl Builder { @@ -287,7 +289,7 @@ impl Builder { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn new() -> Builder { - Builder { name: None, stack_size: None } + Builder { name: None, stack_size: None, no_hooks: false } } /// Names the thread-to-be. Currently the name is used for identification @@ -343,6 +345,16 @@ impl Builder { self } + /// Disables running and inheriting [spawn hooks](add_spawn_hook). + /// + /// Use this if the parent thread is in no way relevant for the child thread. + /// For example, when lazily spawning threads for a thread pool. + #[unstable(feature = "thread_spawn_hook", issue = "none")] + pub fn no_hooks(mut self) -> Builder { + self.no_hooks = true; + self + } + /// Spawns a new thread by taking ownership of the `Builder`, and returns an /// [`io::Result`] to its [`JoinHandle`]. /// @@ -465,7 +477,7 @@ impl Builder { F: Send, T: Send, { - let Builder { name, stack_size } = self; + let Builder { name, stack_size, no_hooks } = self; let stack_size = stack_size.unwrap_or_else(|| { static MIN: AtomicUsize = AtomicUsize::new(0); @@ -491,7 +503,11 @@ impl Builder { None => Thread::new_unnamed(id), }; - let hooks = spawnhook::run_spawn_hooks(&my_thread); + let hooks = if no_hooks { + spawnhook::ChildSpawnHooks::default() + } else { + spawnhook::run_spawn_hooks(&my_thread) + }; let their_thread = my_thread.clone(); diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index 9c4e9eb6aa13a..b0732ae69c302 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -104,7 +104,7 @@ where /// Called on the parent thread. /// /// Returns the functions to be called on the newly spawned thread. -pub(super) fn run_spawn_hooks(thread: &Thread) -> SpawnHookResults { +pub(super) fn run_spawn_hooks(thread: &Thread) -> ChildSpawnHooks { // Get a snapshot of the spawn hooks. // (Increments the refcount to the first node.) let hooks = SPAWN_HOOKS.with(|hooks| { @@ -121,19 +121,20 @@ pub(super) fn run_spawn_hooks(thread: &Thread) -> SpawnHookResults { } // Pass on the snapshot of the hooks and the results to the new thread, // which will then run SpawnHookResults::run(). - SpawnHookResults { hooks, to_run } + ChildSpawnHooks { hooks, to_run } } /// The results of running the spawn hooks. /// /// This struct is sent to the new thread. /// It contains the inherited hooks and the closures to be run. -pub(super) struct SpawnHookResults { +#[derive(Default)] +pub(super) struct ChildSpawnHooks { hooks: SpawnHooks, to_run: Vec>, } -impl SpawnHookResults { +impl ChildSpawnHooks { // This is run on the newly spawned thread, directly at the start. pub(super) fn run(self) { SPAWN_HOOKS.set(self.hooks); From 5a80b48fe17f8ea772125dbc5220a134b1b15c68 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 24 Oct 2024 11:37:35 +0200 Subject: [PATCH 18/22] Use Send + Sync for spawn hooks. --- library/std/src/thread/spawnhook.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index b0732ae69c302..f9a4a2e0da7da 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -24,7 +24,7 @@ impl Drop for SpawnHooks { } struct SpawnHook { - hook: Box Box>, + hook: Box Box>, next: Option>, } @@ -86,7 +86,7 @@ struct SpawnHook { #[unstable(feature = "thread_spawn_hook", issue = "none")] pub fn add_spawn_hook(hook: F) where - F: 'static + Sync + Fn(&Thread) -> G, + F: 'static + Send + Sync + Fn(&Thread) -> G, G: 'static + Send + FnOnce(), { SPAWN_HOOKS.with(|h| { From 24fec0d896ea160ce98113c9600d25f5db4d9827 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 12 Nov 2024 17:18:15 +0100 Subject: [PATCH 19/22] Add tracking issue. --- library/std/src/thread/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 4a4c2c4b96020..2c2cc58a9dda7 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -190,7 +190,7 @@ pub(crate) use current::{current_id, drop_current, set_current, try_current}; mod spawnhook; -#[unstable(feature = "thread_spawn_hook", issue = "none")] +#[unstable(feature = "thread_spawn_hook", issue = "132951")] pub use spawnhook::add_spawn_hook; //////////////////////////////////////////////////////////////////////////////// @@ -349,7 +349,7 @@ impl Builder { /// /// Use this if the parent thread is in no way relevant for the child thread. /// For example, when lazily spawning threads for a thread pool. - #[unstable(feature = "thread_spawn_hook", issue = "none")] + #[unstable(feature = "thread_spawn_hook", issue = "132951")] pub fn no_hooks(mut self) -> Builder { self.no_hooks = true; self From 38b9a448c9a00ca25c6fca352273d819cf92dacb Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 19 Nov 2024 15:19:13 +0100 Subject: [PATCH 20/22] Fix tracking issue. --- library/std/src/thread/spawnhook.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index f9a4a2e0da7da..1513a5036cba1 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -83,7 +83,7 @@ struct SpawnHook { /// assert_eq!(X.get(), 123); /// }).join().unwrap(); /// ``` -#[unstable(feature = "thread_spawn_hook", issue = "none")] +#[unstable(feature = "thread_spawn_hook", issue = "132951")] pub fn add_spawn_hook(hook: F) where F: 'static + Send + Sync + Fn(&Thread) -> G, From b96f023dbd044e70eddd208cd21a295f62e5b28b Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 19 Nov 2024 17:50:42 +0000 Subject: [PATCH 21/22] Address review comments. Co-authored-by: waffle --- library/std/src/thread/spawnhook.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index 1513a5036cba1..b979db6bd6059 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -1,4 +1,5 @@ use crate::cell::Cell; +use crate::iter; use crate::sync::Arc; use crate::thread::Thread; @@ -91,9 +92,10 @@ where { SPAWN_HOOKS.with(|h| { let mut hooks = h.take(); + let next = hooks.first.take(); hooks.first = Some(Arc::new(SpawnHook { hook: Box::new(move |thread| Box::new(hook(thread))), - next: hooks.first.take(), + next, })); h.set(hooks); }); @@ -113,12 +115,9 @@ pub(super) fn run_spawn_hooks(thread: &Thread) -> ChildSpawnHooks { snapshot }); // Iterate over the hooks, run them, and collect the results in a vector. - let mut next: &Option> = &hooks.first; - let mut to_run = Vec::new(); - while let Some(hook) = next { - to_run.push((hook.hook)(thread)); - next = &hook.next; - } + let to_run: Vec<_> = iter::successors(hooks.first.as_deref(), |hook| hook.next.as_deref()) + .map(|hook| (hook.hook)(thread)) + .collect(); // Pass on the snapshot of the hooks and the results to the new thread, // which will then run SpawnHookResults::run(). ChildSpawnHooks { hooks, to_run } From 691796be03d77951982a86f4913994791ed4fb61 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 19 Nov 2024 18:53:39 +0100 Subject: [PATCH 22/22] Update doc comments for spawn hook. --- library/std/src/thread/spawnhook.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index b979db6bd6059..99b5ad9cb9fe5 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -3,8 +3,12 @@ use crate::iter; use crate::sync::Arc; use crate::thread::Thread; -// A thread local linked list of spawn hooks. crate::thread_local! { + /// A thread local linked list of spawn hooks. + /// + /// It is a linked list of Arcs, such that it can very cheaply be inhereted by spawned threads. + /// + /// (That technically makes it a set of linked lists with shared tails, so a linked tree.) static SPAWN_HOOKS: Cell = const { Cell::new(SpawnHooks { first: None }) }; } @@ -42,7 +46,7 @@ struct SpawnHook { /// /// Hooks can only be added, not removed. /// -/// The hooks will run in order, starting with the most recently added. +/// The hooks will run in reverse order, starting with the most recently added. /// /// # Usage ///