authorbors <bors@rust-lang.org> 2025-06-16 14:25:08 UTC
committerbors <bors@rust-lang.org> 2025-06-16 14:25:08 UTC
log3bc767e1a215c4bf8f099b32e84edb85780591b1
tree3da6113318c7c7b0fb401939b8888137c2c7388b
parentd9ca9bd014074e2bac567eaa2b66bfacb2591028
parent78d12b7e56ef5691d31b6f1697aba79eabe4bb2f

Auto merge of #142574 - Kobzol:rollup-ldj386u, r=Kobzol

Rollup of 12 pull requests Successful merges: - rust-lang/rust#141639 (Expose discriminant values in stable_mir) - rust-lang/rust#142082 (Refactor `rustc_attr_data_structures` documentation) - rust-lang/rust#142125 (Stabilize "file_lock" feature) - rust-lang/rust#142236 (Add documentation for `PathBuf`'s `FromIterator` and `Extend` impls) - rust-lang/rust#142373 (Fix Debug for Location) - rust-lang/rust#142416 (Assorted bootstrap cleanups (step 2)) - rust-lang/rust#142431 (Add initial version of snapshot tests to bootstrap) - rust-lang/rust#142450 (Add documentation on top of `rustc_middle/src/query/mod.rs`) - rust-lang/rust#142528 (clarify `rustc_do_not_const_check` comment) - rust-lang/rust#142530 (use `if let` guards where possible) - rust-lang/rust#142561 (Remove an `njn:` comment accidentaly left behind.) - rust-lang/rust#142566 (Fix `-nopt` CI jobs) r? `@ghost` `@rustbot` modify labels: rollup

41 files changed, 721 insertions(+), 231 deletions(-)

compiler/rustc_attr_data_structures/src/attributes.rs+44-14
......@@ -130,24 +130,54 @@ impl Deprecation {
130130 }
131131}
132132
133/// Represent parsed, *built in*, inert attributes.
133/// Represents parsed *built-in* inert attributes.
134134///
135/// That means attributes that are not actually ever expanded.
136/// For more information on this, see the module docs on the [`rustc_attr_parsing`] crate.
137/// They're instead used as markers, to guide the compilation process in various way in most every stage of the compiler.
138/// These are kept around after the AST, into the HIR and further on.
135/// ## Overview
136/// These attributes are markers that guide the compilation process and are never expanded into other code.
137/// They persist throughout the compilation phases, from AST to HIR and beyond.
139138///
140/// The word "parsed" could be a little misleading here, because the parser already parses
141/// attributes early on. However, the result, an [`ast::Attribute`]
142/// is only parsed at a high level, still containing a token stream in many cases. That is
143/// because the structure of the contents varies from attribute to attribute.
144/// With a parsed attribute I mean that each attribute is processed individually into a
145/// final structure, which on-site (the place where the attribute is useful for, think the
146/// the place where `must_use` is checked) little to no extra parsing or validating needs to
147/// happen.
139/// ## Attribute Processing
140/// While attributes are initially parsed by [`rustc_parse`] into [`ast::Attribute`], they still contain raw token streams
141/// because different attributes have different internal structures. This enum represents the final,
142/// fully parsed form of these attributes, where each variant contains contains all the information and
143/// structure relevant for the specific attribute.
148144///
149/// For more docs, look in [`rustc_attr_parsing`].
145/// Some attributes can be applied multiple times to the same item, and they are "collapsed" into a single
146/// semantic attribute. For example:
147/// ```rust
148/// #[repr(C)]
149/// #[repr(packed)]
150/// struct S { }
151/// ```
152/// This is equivalent to `#[repr(C, packed)]` and results in a single [`AttributeKind::Repr`] containing
153/// both `C` and `packed` annotations. This collapsing happens during parsing and is reflected in the
154/// data structures defined in this enum.
150155///
156/// ## Usage
157/// These parsed attributes are used throughout the compiler to:
158/// - Control code generation (e.g., `#[repr]`)
159/// - Mark API stability (`#[stable]`, `#[unstable]`)
160/// - Provide documentation (`#[doc]`)
161/// - Guide compiler behavior (e.g., `#[allow_internal_unstable]`)
162///
163/// ## Note on Attribute Organization
164/// Some attributes like `InlineAttr`, `OptimizeAttr`, and `InstructionSetAttr` are defined separately
165/// from this enum because they are used in specific compiler phases (like code generation) and don't
166/// need to persist throughout the entire compilation process. They are typically processed and
167/// converted into their final form earlier in the compilation pipeline.
168///
169/// For example:
170/// - `InlineAttr` is used during code generation to control function inlining
171/// - `OptimizeAttr` is used to control optimization levels
172/// - `InstructionSetAttr` is used for target-specific code generation
173///
174/// These attributes are handled by their respective compiler passes in the [`rustc_codegen_ssa`] crate
175/// and don't need to be preserved in the same way as the attributes in this enum.
176///
177/// For more details on attribute parsing, see the [`rustc_attr_parsing`] crate.
178///
179/// [`rustc_parse`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html
180/// [`rustc_codegen_ssa`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/index.html
151181/// [`rustc_attr_parsing`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html
152182#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
153183pub enum AttributeKind {
compiler/rustc_attr_data_structures/src/lib.rs+4
......@@ -1,3 +1,7 @@
1//! Data structures for representing parsed attributes in the Rust compiler.
2//! For detailed documentation about attribute processing,
3//! see [rustc_attr_parsing](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html).
4
15// tidy-alphabetical-start
26#![allow(internal_features)]
37#![doc(rust_logo)]
compiler/rustc_builtin_macros/src/test.rs+1-4
......@@ -118,10 +118,7 @@ pub(crate) fn expand_test_or_bench(
118118
119119 let (item, is_stmt) = match item {
120120 Annotatable::Item(i) => (i, false),
121 Annotatable::Stmt(stmt) if matches!(stmt.kind, ast::StmtKind::Item(_)) => {
122 // FIXME: Use an 'if let' guard once they are implemented
123 if let ast::StmtKind::Item(i) = stmt.kind { (i, true) } else { unreachable!() }
124 }
121 Annotatable::Stmt(box ast::Stmt { kind: ast::StmtKind::Item(i), .. }) => (i, true),
125122 other => {
126123 not_testable_error(cx, attr_sp, None);
127124 return vec![other];
compiler/rustc_expand/src/base.rs+2-1
......@@ -35,6 +35,7 @@ use crate::base::ast::MetaItemInner;
3535use crate::errors;
3636use crate::expand::{self, AstFragment, Invocation};
3737use crate::module::DirOwnership;
38use crate::stats::MacroStat;
3839
3940// When adding new variants, make sure to
4041// adjust the `visit_*` / `flat_map_*` calls in `InvocationCollector`
......@@ -1191,7 +1192,7 @@ pub struct ExtCtxt<'a> {
11911192 /// not to expand it again.
11921193 pub(super) expanded_inert_attrs: MarkedAttrs,
11931194 /// `-Zmacro-stats` data.
1194 pub macro_stats: FxHashMap<(Symbol, MacroKind), crate::stats::MacroStat>, // njn: quals
1195 pub macro_stats: FxHashMap<(Symbol, MacroKind), MacroStat>,
11951196}
11961197
11971198impl<'a> ExtCtxt<'a> {
compiler/rustc_feature/src/builtin_attrs.rs+1-1
......@@ -887,7 +887,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
887887 rustc_legacy_const_generics, Normal, template!(List: "N"), ErrorFollowing,
888888 EncodeCrossCrate::Yes,
889889 ),
890 // Do not const-check this function's body. It will always get replaced during CTFE.
890 // Do not const-check this function's body. It will always get replaced during CTFE via `hook_special_const_fn`.
891891 rustc_attr!(
892892 rustc_do_not_const_check, Normal, template!(Word), WarnFollowing,
893893 EncodeCrossCrate::Yes, "`#[rustc_do_not_const_check]` skips const-check for this function's body",
compiler/rustc_middle/src/query/mod.rs+60-4
......@@ -1,8 +1,64 @@
1//! Defines the various compiler queries.
21//!
3//! For more information on the query system, see
4//! ["Queries: demand-driven compilation"](https://rustc-dev-guide.rust-lang.org/query.html).
5//! This chapter includes instructions for adding new queries.
2//! # The rustc Query System: Query Definitions and Modifiers
3//!
4//! The core processes in rustc are shipped as queries. Each query is a demand-driven function from some key to a value.
5//! The execution result of the function is cached and directly read during the next request, thereby improving compilation efficiency.
6//! Some results are saved locally and directly read during the next compilation, which are core of incremental compilation.
7//!
8//! ## How to Read This Module
9//!
10//! Each `query` block in this file defines a single query, specifying its key and value types, along with various modifiers.
11//! These query definitions are processed by the [`rustc_macros`], which expands them into the necessary boilerplate code
12//! for the query system—including the [`Providers`] struct (a function table for all query implementations, where each field is
13//! a function pointer to the actual provider), caching, and dependency graph integration.
14//! **Note:** The `Providers` struct is not a Rust trait, but a struct generated by the `rustc_macros` to hold all provider functions.
15//! The `rustc_macros` also supports a set of **query modifiers** (see below) that control the behavior of each query.
16//!
17//! The actual provider functions are implemented in various modules and registered into the `Providers` struct
18//! during compiler initialization (see [`rustc_interface::passes::DEFAULT_QUERY_PROVIDERS`]).
19//!
20//! [`rustc_macros`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_macros/index.html
21//! [`rustc_interface::passes::DEFAULT_QUERY_PROVIDERS`]: ../../rustc_interface/passes/static.DEFAULT_QUERY_PROVIDERS.html
22//!
23//! ## Query Modifiers
24//!
25//! Query modifiers are special flags that alter the behavior of a query. They are parsed and processed by the `rustc_macros`
26//! The main modifiers are:
27//!
28//! - `desc { ... }`: Sets the human-readable description for diagnostics and profiling. Required for every query.
29//! - `arena_cache`: Use an arena for in-memory caching of the query result.
30//! - `cache_on_disk_if { ... }`: Cache the query result to disk if the provided block evaluates to true.
31//! - `fatal_cycle`: If a dependency cycle is detected, abort compilation with a fatal error.
32//! - `cycle_delay_bug`: If a dependency cycle is detected, emit a delayed bug instead of aborting immediately.
33//! - `cycle_stash`: If a dependency cycle is detected, stash the error for later handling.
34//! - `no_hash`: Do not hash the query result for incremental compilation; just mark as dirty if recomputed.
35//! - `anon`: Make the query anonymous in the dependency graph (no dep node is created).
36//! - `eval_always`: Always evaluate the query, ignoring its dependencies and cached results.
37//! - `depth_limit`: Impose a recursion depth limit on the query to prevent stack overflows.
38//! - `separate_provide_extern`: Use separate provider functions for local and external crates.
39//! - `feedable`: Allow the query result to be set from another query ("fed" externally).
40//! - `return_result_from_ensure_ok`: When called via `tcx.ensure_ok()`, return `Result<(), ErrorGuaranteed>` instead of `()`.
41//! If the query needs to be executed and returns an error, the error is returned to the caller.
42//! Only valid for queries returning `Result<_, ErrorGuaranteed>`.
43//!
44//! For the up-to-date list, see the `QueryModifiers` struct in
45//! [`rustc_macros/src/query.rs`](https://github.com/rust-lang/rust/blob/master/compiler/rustc_macros/src/query.rs)
46//! and for more details in incremental compilation, see the
47//! [Query modifiers in incremental compilation](https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation-in-detail.html#query-modifiers) section of the rustc-dev-guide.
48//!
49//! ## Query Expansion and Code Generation
50//!
51//! The [`rustc_macros::rustc_queries`] macro expands each query definition into:
52//! - A method on [`TyCtxt`] (and [`TyCtxtAt`]) for invoking the query.
53//! - Provider traits and structs for supplying the query's value.
54//! - Caching and dependency graph integration.
55//! - Support for incremental compilation, disk caching, and arena allocation as controlled by the modifiers.
56//!
57//! [`rustc_macros::rustc_queries`]: ../../rustc_macros/macro.rustc_queries.html
58//!
59//! The macro-based approach allows the query system to be highly flexible and maintainable, while minimizing boilerplate.
60//!
61//! For more details, see the [rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/query.html).
662
763#![allow(unused_parens)]
864
compiler/rustc_parse/src/parser/diagnostics.rs+11-16
......@@ -2273,23 +2273,18 @@ impl<'a> Parser<'a> {
22732273 ),
22742274 // Also catches `fn foo(&a)`.
22752275 PatKind::Ref(ref inner_pat, mutab)
2276 if matches!(inner_pat.clone().kind, PatKind::Ident(..)) =>
2276 if let PatKind::Ident(_, ident, _) = inner_pat.clone().kind =>
22772277 {
2278 match inner_pat.clone().kind {
2279 PatKind::Ident(_, ident, _) => {
2280 let mutab = mutab.prefix_str();
2281 (
2282 ident,
2283 "self: ",
2284 format!("{ident}: &{mutab}TypeName"),
2285 "_: ",
2286 pat.span.shrink_to_lo(),
2287 pat.span,
2288 pat.span.shrink_to_lo(),
2289 )
2290 }
2291 _ => unreachable!(),
2292 }
2278 let mutab = mutab.prefix_str();
2279 (
2280 ident,
2281 "self: ",
2282 format!("{ident}: &{mutab}TypeName"),
2283 "_: ",
2284 pat.span.shrink_to_lo(),
2285 pat.span,
2286 pat.span.shrink_to_lo(),
2287 )
22932288 }
22942289 _ => {
22952290 // Otherwise, try to get a type and emit a suggestion.
compiler/rustc_smir/src/rustc_smir/context.rs+29-4
......@@ -12,7 +12,8 @@ use rustc_middle::ty::layout::{
1212};
1313use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
1414use rustc_middle::ty::{
15 GenericPredicates, Instance, List, ScalarInt, TyCtxt, TypeVisitableExt, ValTree,
15 CoroutineArgsExt, GenericPredicates, Instance, List, ScalarInt, TyCtxt, TypeVisitableExt,
16 ValTree,
1617};
1718use rustc_middle::{mir, ty};
1819use rustc_span::def_id::LOCAL_CRATE;
......@@ -22,9 +23,9 @@ use stable_mir::mir::mono::{InstanceDef, StaticDef};
2223use stable_mir::mir::{BinOp, Body, Place, UnOp};
2324use stable_mir::target::{MachineInfo, MachineSize};
2425use stable_mir::ty::{
25 AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef,
26 ForeignItemKind, GenericArgs, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, Ty,
27 TyConst, TyKind, UintTy, VariantDef,
26 AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, CoroutineDef, Discr, FieldDef, FnDef,
27 ForeignDef, ForeignItemKind, GenericArgs, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy,
28 Span, Ty, TyConst, TyKind, UintTy, VariantDef, VariantIdx,
2829};
2930use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, ItemKind, Symbol};
3031
......@@ -447,6 +448,30 @@ impl<'tcx> SmirCtxt<'tcx> {
447448 def.internal(&mut *tables, tcx).variants().len()
448449 }
449450
451 /// Discriminant for a given variant index of AdtDef
452 pub fn adt_discr_for_variant(&self, adt: AdtDef, variant: VariantIdx) -> Discr {
453 let mut tables = self.0.borrow_mut();
454 let tcx = tables.tcx;
455 let adt = adt.internal(&mut *tables, tcx);
456 let variant = variant.internal(&mut *tables, tcx);
457 adt.discriminant_for_variant(tcx, variant).stable(&mut *tables)
458 }
459
460 /// Discriminant for a given variand index and args of a coroutine
461 pub fn coroutine_discr_for_variant(
462 &self,
463 coroutine: CoroutineDef,
464 args: &GenericArgs,
465 variant: VariantIdx,
466 ) -> Discr {
467 let mut tables = self.0.borrow_mut();
468 let tcx = tables.tcx;
469 let coroutine = coroutine.def_id().internal(&mut *tables, tcx);
470 let args = args.internal(&mut *tables, tcx);
471 let variant = variant.internal(&mut *tables, tcx);
472 args.as_coroutine().discriminant_for_variant(coroutine, tcx, variant).stable(&mut *tables)
473 }
474
450475 /// The name of a variant.
451476 pub fn variant_name(&self, def: VariantDef) -> Symbol {
452477 let mut tables = self.0.borrow_mut();
compiler/rustc_smir/src/rustc_smir/convert/ty.rs+8
......@@ -960,3 +960,11 @@ impl<'tcx> Stable<'tcx> for ty::ImplTraitInTraitData {
960960 }
961961 }
962962}
963
964impl<'tcx> Stable<'tcx> for rustc_middle::ty::util::Discr<'tcx> {
965 type T = stable_mir::ty::Discr;
966
967 fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
968 stable_mir::ty::Discr { val: self.val, ty: self.ty.stable(tables) }
969 }
970}
compiler/rustc_smir/src/stable_mir/compiler_interface.rs+19-4
......@@ -13,10 +13,10 @@ use stable_mir::mir::mono::{Instance, InstanceDef, StaticDef};
1313use stable_mir::mir::{BinOp, Body, Place, UnOp};
1414use stable_mir::target::MachineInfo;
1515use stable_mir::ty::{
16 AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef,
17 ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics,
18 ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, TraitDecl,
19 TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef,
16 AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, CoroutineDef, Discr, FieldDef, FnDef,
17 ForeignDef, ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates,
18 Generics, ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span,
19 TraitDecl, TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, VariantIdx,
2020};
2121use stable_mir::{
2222 AssocItems, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls,
......@@ -230,6 +230,21 @@ impl<'tcx> SmirInterface<'tcx> {
230230 self.cx.adt_variants_len(def)
231231 }
232232
233 /// Discriminant for a given variant index of AdtDef
234 pub(crate) fn adt_discr_for_variant(&self, adt: AdtDef, variant: VariantIdx) -> Discr {
235 self.cx.adt_discr_for_variant(adt, variant)
236 }
237
238 /// Discriminant for a given variand index and args of a coroutine
239 pub(crate) fn coroutine_discr_for_variant(
240 &self,
241 coroutine: CoroutineDef,
242 args: &GenericArgs,
243 variant: VariantIdx,
244 ) -> Discr {
245 self.cx.coroutine_discr_for_variant(coroutine, args, variant)
246 }
247
233248 /// The name of a variant.
234249 pub(crate) fn variant_name(&self, def: VariantDef) -> Symbol {
235250 self.cx.variant_name(def)
compiler/rustc_smir/src/stable_mir/ty.rs+15
......@@ -756,6 +756,12 @@ crate_def! {
756756 pub CoroutineDef;
757757}
758758
759impl CoroutineDef {
760 pub fn discriminant_for_variant(&self, args: &GenericArgs, idx: VariantIdx) -> Discr {
761 with(|cx| cx.coroutine_discr_for_variant(*self, args, idx))
762 }
763}
764
759765crate_def! {
760766 #[derive(Serialize)]
761767 pub CoroutineClosureDef;
......@@ -831,6 +837,15 @@ impl AdtDef {
831837 pub fn repr(&self) -> ReprOptions {
832838 with(|cx| cx.adt_repr(*self))
833839 }
840
841 pub fn discriminant_for_variant(&self, idx: VariantIdx) -> Discr {
842 with(|cx| cx.adt_discr_for_variant(*self, idx))
843 }
844}
845
846pub struct Discr {
847 pub val: u128,
848 pub ty: Ty,
834849}
835850
836851/// Definition of a variant, which can be either a struct / union field or an enum variant.
library/core/src/panic/location.rs+12-1
......@@ -30,7 +30,7 @@ use crate::fmt;
3030/// Files are compared as strings, not `Path`, which could be unexpected.
3131/// See [`Location::file`]'s documentation for more discussion.
3232#[lang = "panic_location"]
33#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
33#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
3434#[stable(feature = "panic_hooks", since = "1.10.0")]
3535pub struct Location<'a> {
3636 // Note: this filename will have exactly one nul byte at its end, but otherwise
......@@ -43,6 +43,17 @@ pub struct Location<'a> {
4343 col: u32,
4444}
4545
46#[stable(feature = "panic_hooks", since = "1.10.0")]
47impl fmt::Debug for Location<'_> {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 f.debug_struct("Location")
50 .field("file", &self.file())
51 .field("line", &self.line)
52 .field("column", &self.col)
53 .finish()
54 }
55}
56
4657impl<'a> Location<'a> {
4758 /// Returns the source location of the caller of this function. If that function's caller is
4859 /// annotated then its call location will be returned, and so on up the stack to the first call
library/coretests/tests/panic/location.rs+8
......@@ -29,3 +29,11 @@ fn location_const_column() {
2929 const COLUMN: u32 = CALLER.column();
3030 assert_eq!(COLUMN, 40);
3131}
32
33#[test]
34fn location_debug() {
35 let f = format!("{:?}", Location::caller());
36 assert!(f.contains(&format!("{:?}", file!())));
37 assert!(f.contains("35"));
38 assert!(f.contains("29"));
39}
library/std/src/fs.rs+10-15
......@@ -121,7 +121,7 @@ pub struct File {
121121///
122122/// [`try_lock`]: File::try_lock
123123/// [`try_lock_shared`]: File::try_lock_shared
124#[unstable(feature = "file_lock", issue = "130994")]
124#[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")]
125125pub enum TryLockError {
126126 /// The lock could not be acquired due to an I/O error on the file. The standard library will
127127 /// not return an [`ErrorKind::WouldBlock`] error inside [`TryLockError::Error`]
......@@ -366,10 +366,10 @@ pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result
366366 inner(path.as_ref(), contents.as_ref())
367367}
368368
369#[unstable(feature = "file_lock", issue = "130994")]
369#[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")]
370370impl error::Error for TryLockError {}
371371
372#[unstable(feature = "file_lock", issue = "130994")]
372#[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")]
373373impl fmt::Debug for TryLockError {
374374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375375 match self {
......@@ -379,7 +379,7 @@ impl fmt::Debug for TryLockError {
379379 }
380380}
381381
382#[unstable(feature = "file_lock", issue = "130994")]
382#[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")]
383383impl fmt::Display for TryLockError {
384384 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
385385 match self {
......@@ -390,7 +390,7 @@ impl fmt::Display for TryLockError {
390390 }
391391}
392392
393#[unstable(feature = "file_lock", issue = "130994")]
393#[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")]
394394impl From<TryLockError> for io::Error {
395395 fn from(err: TryLockError) -> io::Error {
396396 match err {
......@@ -713,7 +713,6 @@ impl File {
713713 /// # Examples
714714 ///
715715 /// ```no_run
716 /// #![feature(file_lock)]
717716 /// use std::fs::File;
718717 ///
719718 /// fn main() -> std::io::Result<()> {
......@@ -722,7 +721,7 @@ impl File {
722721 /// Ok(())
723722 /// }
724723 /// ```
725 #[unstable(feature = "file_lock", issue = "130994")]
724 #[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")]
726725 pub fn lock(&self) -> io::Result<()> {
727726 self.inner.lock()
728727 }
......@@ -766,7 +765,6 @@ impl File {
766765 /// # Examples
767766 ///
768767 /// ```no_run
769 /// #![feature(file_lock)]
770768 /// use std::fs::File;
771769 ///
772770 /// fn main() -> std::io::Result<()> {
......@@ -775,7 +773,7 @@ impl File {
775773 /// Ok(())
776774 /// }
777775 /// ```
778 #[unstable(feature = "file_lock", issue = "130994")]
776 #[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")]
779777 pub fn lock_shared(&self) -> io::Result<()> {
780778 self.inner.lock_shared()
781779 }
......@@ -824,7 +822,6 @@ impl File {
824822 /// # Examples
825823 ///
826824 /// ```no_run
827 /// #![feature(file_lock)]
828825 /// use std::fs::{File, TryLockError};
829826 ///
830827 /// fn main() -> std::io::Result<()> {
......@@ -840,7 +837,7 @@ impl File {
840837 /// Ok(())
841838 /// }
842839 /// ```
843 #[unstable(feature = "file_lock", issue = "130994")]
840 #[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")]
844841 pub fn try_lock(&self) -> Result<(), TryLockError> {
845842 self.inner.try_lock()
846843 }
......@@ -888,7 +885,6 @@ impl File {
888885 /// # Examples
889886 ///
890887 /// ```no_run
891 /// #![feature(file_lock)]
892888 /// use std::fs::{File, TryLockError};
893889 ///
894890 /// fn main() -> std::io::Result<()> {
......@@ -905,7 +901,7 @@ impl File {
905901 /// Ok(())
906902 /// }
907903 /// ```
908 #[unstable(feature = "file_lock", issue = "130994")]
904 #[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")]
909905 pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
910906 self.inner.try_lock_shared()
911907 }
......@@ -933,7 +929,6 @@ impl File {
933929 /// # Examples
934930 ///
935931 /// ```no_run
936 /// #![feature(file_lock)]
937932 /// use std::fs::File;
938933 ///
939934 /// fn main() -> std::io::Result<()> {
......@@ -943,7 +938,7 @@ impl File {
943938 /// Ok(())
944939 /// }
945940 /// ```
946 #[unstable(feature = "file_lock", issue = "130994")]
941 #[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")]
947942 pub fn unlock(&self) -> io::Result<()> {
948943 self.inner.unlock()
949944 }
library/std/src/path.rs+27
......@@ -1882,6 +1882,19 @@ impl FromStr for PathBuf {
18821882
18831883#[stable(feature = "rust1", since = "1.0.0")]
18841884impl<P: AsRef<Path>> FromIterator<P> for PathBuf {
1885 /// Creates a new `PathBuf` from the [`Path`] elements of an iterator.
1886 ///
1887 /// This uses [`push`](Self::push) to add each element, so can be used to adjoin multiple path
1888 /// [components](Components).
1889 ///
1890 /// # Examples
1891 /// ```
1892 /// # use std::path::PathBuf;
1893 /// let path = PathBuf::from_iter(["/tmp", "foo", "bar"]);
1894 /// assert_eq!(path, PathBuf::from("/tmp/foo/bar"));
1895 /// ```
1896 ///
1897 /// See documentation for [`push`](Self::push) for more details on how the path is constructed.
18851898 fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> PathBuf {
18861899 let mut buf = PathBuf::new();
18871900 buf.extend(iter);
......@@ -1891,6 +1904,20 @@ impl<P: AsRef<Path>> FromIterator<P> for PathBuf {
18911904
18921905#[stable(feature = "rust1", since = "1.0.0")]
18931906impl<P: AsRef<Path>> Extend<P> for PathBuf {
1907 /// Extends `self` with [`Path`] elements from `iter`.
1908 ///
1909 /// This uses [`push`](Self::push) to add each element, so can be used to adjoin multiple path
1910 /// [components](Components).
1911 ///
1912 /// # Examples
1913 /// ```
1914 /// # use std::path::PathBuf;
1915 /// let mut path = PathBuf::from("/tmp");
1916 /// path.extend(["foo", "bar", "file.txt"]);
1917 /// assert_eq!(path, PathBuf::from("/tmp/foo/bar/file.txt"));
1918 /// ```
1919 ///
1920 /// See documentation for [`push`](Self::push) for more details on how the path is constructed.
18941921 fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
18951922 iter.into_iter().for_each(move |p| self.push(p.as_ref()));
18961923 }
src/bootstrap/Cargo.lock+36
......@@ -44,6 +44,7 @@ dependencies = [
4444 "fd-lock",
4545 "home",
4646 "ignore",
47 "insta",
4748 "junction",
4849 "libc",
4950 "object",
......@@ -158,6 +159,18 @@ dependencies = [
158159 "cc",
159160]
160161
162[[package]]
163name = "console"
164version = "0.15.11"
165source = "registry+https://github.com/rust-lang/crates.io-index"
166checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
167dependencies = [
168 "encode_unicode",
169 "libc",
170 "once_cell",
171 "windows-sys 0.59.0",
172]
173
161174[[package]]
162175name = "cpufeatures"
163176version = "0.2.15"
......@@ -218,6 +231,12 @@ dependencies = [
218231 "crypto-common",
219232]
220233
234[[package]]
235name = "encode_unicode"
236version = "1.0.0"
237source = "registry+https://github.com/rust-lang/crates.io-index"
238checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
239
221240[[package]]
222241name = "errno"
223242version = "0.3.11"
......@@ -323,6 +342,17 @@ dependencies = [
323342 "winapi-util",
324343]
325344
345[[package]]
346name = "insta"
347version = "1.43.1"
348source = "registry+https://github.com/rust-lang/crates.io-index"
349checksum = "154934ea70c58054b556dd430b99a98c2a7ff5309ac9891597e339b5c28f4371"
350dependencies = [
351 "console",
352 "once_cell",
353 "similar",
354]
355
326356[[package]]
327357name = "itoa"
328358version = "1.0.11"
......@@ -675,6 +705,12 @@ version = "1.3.0"
675705source = "registry+https://github.com/rust-lang/crates.io-index"
676706checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
677707
708[[package]]
709name = "similar"
710version = "2.7.0"
711source = "registry+https://github.com/rust-lang/crates.io-index"
712checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
713
678714[[package]]
679715name = "smallvec"
680716version = "1.13.2"
src/bootstrap/Cargo.toml+1
......@@ -84,6 +84,7 @@ features = [
8484[dev-dependencies]
8585pretty_assertions = "1.4"
8686tempfile = "3.15.0"
87insta = "1.43"
8788
8889# We care a lot about bootstrap's compile times, so don't include debuginfo for
8990# dependencies, only bootstrap itself.
src/bootstrap/README.md+4
......@@ -200,6 +200,10 @@ please file issues on the [Rust issue tracker][rust-issue-tracker].
200200[rust-bootstrap-zulip]: https://rust-lang.zulipchat.com/#narrow/stream/t-infra.2Fbootstrap
201201[rust-issue-tracker]: https://github.com/rust-lang/rust/issues
202202
203## Testing
204
205To run bootstrap tests, execute `x test bootstrap`. If you want to bless snapshot tests, then install `cargo-insta` (`cargo install cargo-insta`) and then run `cargo insta review --manifest-path src/bootstrap/Cargo.toml`.
206
203207## Changelog
204208
205209Because we do not release bootstrap with versions, we also do not maintain CHANGELOG files. To
src/bootstrap/src/core/build_steps/test.rs+4-3
......@@ -2009,7 +2009,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
20092009 // Note that if we encounter `PATH` we make sure to append to our own `PATH`
20102010 // rather than stomp over it.
20112011 if !builder.config.dry_run() && target.is_msvc() {
2012 for (k, v) in builder.cc.borrow()[&target].env() {
2012 for (k, v) in builder.cc[&target].env() {
20132013 if k != "PATH" {
20142014 cmd.env(k, v);
20152015 }
......@@ -2026,8 +2026,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
20262026 // address sanitizer enabled (e.g., ntdll.dll).
20272027 cmd.env("ASAN_WIN_CONTINUE_ON_INTERCEPTION_FAILURE", "1");
20282028 // Add the address sanitizer runtime to the PATH - it is located next to cl.exe.
2029 let asan_runtime_path =
2030 builder.cc.borrow()[&target].path().parent().unwrap().to_path_buf();
2029 let asan_runtime_path = builder.cc[&target].path().parent().unwrap().to_path_buf();
20312030 let old_path = cmd
20322031 .get_envs()
20332032 .find_map(|(k, v)| (k == "PATH").then_some(v))
......@@ -3059,6 +3058,8 @@ impl Step for Bootstrap {
30593058 cargo
30603059 .rustflag("-Cdebuginfo=2")
30613060 .env("CARGO_TARGET_DIR", builder.out.join("bootstrap"))
3061 // Needed for insta to correctly write pending snapshots to the right directories.
3062 .env("INSTA_WORKSPACE_ROOT", &builder.src)
30623063 .env("RUSTC_BOOTSTRAP", "1");
30633064
30643065 // bootstrap tests are racy on directory creation so just run them one at a time.
src/bootstrap/src/core/build_steps/tool.rs+1-1
......@@ -1334,7 +1334,7 @@ impl Builder<'_> {
13341334 if compiler.host.is_msvc() {
13351335 let curpaths = env::var_os("PATH").unwrap_or_default();
13361336 let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>();
1337 for (k, v) in self.cc.borrow()[&compiler.host].env() {
1337 for (k, v) in self.cc[&compiler.host].env() {
13381338 if k != "PATH" {
13391339 continue;
13401340 }
src/bootstrap/src/core/builder/cargo.rs+1-3
......@@ -278,9 +278,7 @@ impl Cargo {
278278 self.rustdocflags.arg(&arg);
279279 }
280280
281 if !builder.config.dry_run()
282 && builder.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz")
283 {
281 if !builder.config.dry_run() && builder.cc[&target].args().iter().any(|arg| arg == "-gz") {
284282 self.rustflags.arg("-Clink-arg=-gz");
285283 }
286284
src/bootstrap/src/core/builder/mod.rs+19-2
......@@ -5,7 +5,7 @@ use std::fmt::{self, Debug, Write};
55use std::hash::Hash;
66use std::ops::Deref;
77use std::path::{Path, PathBuf};
8use std::sync::LazyLock;
8use std::sync::{LazyLock, OnceLock};
99use std::time::{Duration, Instant};
1010use std::{env, fs};
1111
......@@ -60,6 +60,9 @@ pub struct Builder<'a> {
6060 /// to do. For example: with `./x check foo bar` we get `paths=["foo",
6161 /// "bar"]`.
6262 pub paths: Vec<PathBuf>,
63
64 /// Cached list of submodules from self.build.src.
65 submodule_paths_cache: OnceLock<Vec<String>>,
6366}
6467
6568impl Deref for Builder<'_> {
......@@ -687,7 +690,7 @@ impl<'a> ShouldRun<'a> {
687690 ///
688691 /// [`path`]: ShouldRun::path
689692 pub fn paths(mut self, paths: &[&str]) -> Self {
690 let submodules_paths = build_helper::util::parse_gitmodules(&self.builder.src);
693 let submodules_paths = self.builder.submodule_paths();
691694
692695 self.paths.insert(PathSet::Set(
693696 paths
......@@ -1180,6 +1183,7 @@ impl<'a> Builder<'a> {
11801183 stack: RefCell::new(Vec::new()),
11811184 time_spent_on_dependencies: Cell::new(Duration::new(0, 0)),
11821185 paths,
1186 submodule_paths_cache: Default::default(),
11831187 }
11841188 }
11851189
......@@ -1510,6 +1514,19 @@ impl<'a> Builder<'a> {
15101514 None
15111515 }
15121516
1517 /// Updates all submodules, and exits with an error if submodule
1518 /// management is disabled and the submodule does not exist.
1519 pub fn require_and_update_all_submodules(&self) {
1520 for submodule in self.submodule_paths() {
1521 self.require_submodule(submodule, None);
1522 }
1523 }
1524
1525 /// Get all submodules from the src directory.
1526 pub fn submodule_paths(&self) -> &[String] {
1527 self.submodule_paths_cache.get_or_init(|| build_helper::util::parse_gitmodules(&self.src))
1528 }
1529
15131530 /// Ensure that a given step is built, returning its output. This will
15141531 /// cache the step, so it is safe (and good!) to call this as often as
15151532 /// needed to ensure that all dependencies are built.
src/bootstrap/src/core/builder/tests.rs+86-10
......@@ -15,11 +15,12 @@ static TEST_TRIPLE_2: &str = "i686-unknown-hurd-gnu";
1515static TEST_TRIPLE_3: &str = "i686-unknown-netbsd";
1616
1717fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config {
18 configure_with_args(&[cmd.to_owned()], host, target)
18 configure_with_args(&[cmd], host, target)
1919}
2020
21fn configure_with_args(cmd: &[String], host: &[&str], target: &[&str]) -> Config {
22 let mut config = Config::parse(Flags::parse(cmd));
21fn configure_with_args(cmd: &[&str], host: &[&str], target: &[&str]) -> Config {
22 let cmd = cmd.iter().copied().map(String::from).collect::<Vec<_>>();
23 let mut config = Config::parse(Flags::parse(&cmd));
2324 // don't save toolstates
2425 config.save_toolstates = None;
2526 config.set_dry_run(DryRun::SelfCheck);
......@@ -67,7 +68,7 @@ fn run_build(paths: &[PathBuf], config: Config) -> Cache {
6768fn check_cli<const N: usize>(paths: [&str; N]) {
6869 run_build(
6970 &paths.map(PathBuf::from),
70 configure_with_args(&paths.map(String::from), &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]),
71 configure_with_args(&paths, &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]),
7172 );
7273}
7374
......@@ -1000,8 +1001,7 @@ mod sysroot_target_dirs {
10001001/// cg_gcc tests instead.
10011002#[test]
10021003fn test_test_compiler() {
1003 let cmd = &["test", "compiler"].map(str::to_owned);
1004 let config = configure_with_args(cmd, &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]);
1004 let config = configure_with_args(&["test", "compiler"], &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]);
10051005 let cache = run_build(&config.paths.clone(), config);
10061006
10071007 let compiler = cache.contains::<test::CrateLibrustc>();
......@@ -1034,8 +1034,7 @@ fn test_test_coverage() {
10341034 // Print each test case so that if one fails, the most recently printed
10351035 // case is the one that failed.
10361036 println!("Testing case: {cmd:?}");
1037 let cmd = cmd.iter().copied().map(str::to_owned).collect::<Vec<_>>();
1038 let config = configure_with_args(&cmd, &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]);
1037 let config = configure_with_args(cmd, &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]);
10391038 let mut cache = run_build(&config.paths.clone(), config);
10401039
10411040 let modes =
......@@ -1207,8 +1206,7 @@ fn test_get_tool_rustc_compiler() {
12071206/// of `Any { .. }`.
12081207#[test]
12091208fn step_cycle_debug() {
1210 let cmd = ["run", "cyclic-step"].map(str::to_owned);
1211 let config = configure_with_args(&cmd, &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]);
1209 let config = configure_with_args(&["run", "cyclic-step"], &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]);
12121210
12131211 let err = panic::catch_unwind(|| run_build(&config.paths.clone(), config)).unwrap_err();
12141212 let err = err.downcast_ref::<String>().unwrap().as_str();
......@@ -1233,3 +1231,81 @@ fn any_debug() {
12331231 // Downcasting to the underlying type should succeed.
12341232 assert_eq!(x.downcast_ref::<MyStruct>(), Some(&MyStruct { x: 7 }));
12351233}
1234
1235/// The staging tests use insta for snapshot testing.
1236/// See bootstrap's README on how to bless the snapshots.
1237mod staging {
1238 use crate::core::builder::tests::{
1239 TEST_TRIPLE_1, configure, configure_with_args, render_steps, run_build,
1240 };
1241
1242 #[test]
1243 fn build_compiler_stage_1() {
1244 let mut cache = run_build(
1245 &["compiler".into()],
1246 configure_with_args(&["build", "--stage", "1"], &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]),
1247 );
1248 let steps = cache.into_executed_steps();
1249 insta::assert_snapshot!(render_steps(&steps), @r"
1250 [build] rustc 0 <target1> -> std 0 <target1>
1251 [build] llvm <target1>
1252 [build] rustc 0 <target1> -> rustc 1 <target1>
1253 [build] rustc 0 <target1> -> rustc 1 <target1>
1254 ");
1255 }
1256}
1257
1258/// Renders the executed bootstrap steps for usage in snapshot tests with insta.
1259/// Only renders certain important steps.
1260/// Each value in `steps` should be a tuple of (Step, step output).
1261fn render_steps(steps: &[(Box<dyn Any>, Box<dyn Any>)]) -> String {
1262 steps
1263 .iter()
1264 .filter_map(|(step, output)| {
1265 // FIXME: implement an optional method on Step to produce metadata for test, instead
1266 // of this downcasting
1267 if let Some((rustc, output)) = downcast_step::<compile::Rustc>(step, output) {
1268 Some(format!(
1269 "[build] {} -> {}",
1270 render_compiler(rustc.build_compiler),
1271 // FIXME: return the correct stage from the `Rustc` step, now it behaves weirdly
1272 render_compiler(Compiler::new(rustc.build_compiler.stage + 1, rustc.target)),
1273 ))
1274 } else if let Some((std, output)) = downcast_step::<compile::Std>(step, output) {
1275 Some(format!(
1276 "[build] {} -> std {} <{}>",
1277 render_compiler(std.compiler),
1278 std.compiler.stage,
1279 std.target
1280 ))
1281 } else if let Some((llvm, output)) = downcast_step::<llvm::Llvm>(step, output) {
1282 Some(format!("[build] llvm <{}>", llvm.target))
1283 } else {
1284 None
1285 }
1286 })
1287 .map(|line| {
1288 line.replace(TEST_TRIPLE_1, "target1")
1289 .replace(TEST_TRIPLE_2, "target2")
1290 .replace(TEST_TRIPLE_3, "target3")
1291 })
1292 .collect::<Vec<_>>()
1293 .join("\n")
1294}
1295
1296fn downcast_step<'a, S: Step>(
1297 step: &'a Box<dyn Any>,
1298 output: &'a Box<dyn Any>,
1299) -> Option<(&'a S, &'a S::Output)> {
1300 let Some(step) = step.downcast_ref::<S>() else {
1301 return None;
1302 };
1303 let Some(output) = output.downcast_ref::<S::Output>() else {
1304 return None;
1305 };
1306 Some((step, output))
1307}
1308
1309fn render_compiler(compiler: Compiler) -> String {
1310 format!("rustc {} <{}>", compiler.stage, compiler.host)
1311}
src/bootstrap/src/lib.rs+22-27
......@@ -17,7 +17,7 @@
1717//! also check out the `src/bootstrap/README.md` file for more information.
1818#![cfg_attr(test, allow(unused))]
1919
20use std::cell::{Cell, RefCell};
20use std::cell::Cell;
2121use std::collections::{BTreeSet, HashMap, HashSet};
2222use std::fmt::Display;
2323use std::path::{Path, PathBuf};
......@@ -189,10 +189,12 @@ pub struct Build {
189189
190190 // Runtime state filled in later on
191191 // C/C++ compilers and archiver for all targets
192 cc: RefCell<HashMap<TargetSelection, cc::Tool>>,
193 cxx: RefCell<HashMap<TargetSelection, cc::Tool>>,
194 ar: RefCell<HashMap<TargetSelection, PathBuf>>,
195 ranlib: RefCell<HashMap<TargetSelection, PathBuf>>,
192 cc: HashMap<TargetSelection, cc::Tool>,
193 cxx: HashMap<TargetSelection, cc::Tool>,
194 ar: HashMap<TargetSelection, PathBuf>,
195 ranlib: HashMap<TargetSelection, PathBuf>,
196 wasi_sdk_path: Option<PathBuf>,
197
196198 // Miscellaneous
197199 // allow bidirectional lookups: both name -> path and path -> name
198200 crates: HashMap<String, Crate>,
......@@ -466,10 +468,11 @@ impl Build {
466468 enzyme_info,
467469 in_tree_llvm_info,
468470 in_tree_gcc_info,
469 cc: RefCell::new(HashMap::new()),
470 cxx: RefCell::new(HashMap::new()),
471 ar: RefCell::new(HashMap::new()),
472 ranlib: RefCell::new(HashMap::new()),
471 cc: HashMap::new(),
472 cxx: HashMap::new(),
473 ar: HashMap::new(),
474 ranlib: HashMap::new(),
475 wasi_sdk_path: env::var_os("WASI_SDK_PATH").map(PathBuf::from),
473476 crates: HashMap::new(),
474477 crate_paths: HashMap::new(),
475478 is_sudo,
......@@ -498,7 +501,7 @@ impl Build {
498501 }
499502
500503 build.verbose(|| println!("finding compilers"));
501 utils::cc_detect::find(&build);
504 utils::cc_detect::fill_compilers(&mut build);
502505 // When running `setup`, the profile is about to change, so any requirements we have now may
503506 // be different on the next invocation. Don't check for them until the next time x.py is
504507 // run. This is ok because `setup` never runs any build commands, so it won't fail if commands are missing.
......@@ -593,14 +596,6 @@ impl Build {
593596 }
594597 }
595598
596 /// Updates all submodules, and exits with an error if submodule
597 /// management is disabled and the submodule does not exist.
598 pub fn require_and_update_all_submodules(&self) {
599 for submodule in build_helper::util::parse_gitmodules(&self.src) {
600 self.require_submodule(submodule, None);
601 }
602 }
603
604599 /// If any submodule has been initialized already, sync it unconditionally.
605600 /// This avoids contributors checking in a submodule change by accident.
606601 fn update_existing_submodules(&self) {
......@@ -1143,17 +1138,17 @@ impl Build {
11431138 if self.config.dry_run() {
11441139 return PathBuf::new();
11451140 }
1146 self.cc.borrow()[&target].path().into()
1141 self.cc[&target].path().into()
11471142 }
11481143
11491144 /// Returns the internal `cc::Tool` for the C compiler.
11501145 fn cc_tool(&self, target: TargetSelection) -> Tool {
1151 self.cc.borrow()[&target].clone()
1146 self.cc[&target].clone()
11521147 }
11531148
11541149 /// Returns the internal `cc::Tool` for the C++ compiler.
11551150 fn cxx_tool(&self, target: TargetSelection) -> Tool {
1156 self.cxx.borrow()[&target].clone()
1151 self.cxx[&target].clone()
11571152 }
11581153
11591154 /// Returns C flags that `cc-rs` thinks should be enabled for the
......@@ -1163,8 +1158,8 @@ impl Build {
11631158 return Vec::new();
11641159 }
11651160 let base = match c {
1166 CLang::C => self.cc.borrow()[&target].clone(),
1167 CLang::Cxx => self.cxx.borrow()[&target].clone(),
1161 CLang::C => self.cc[&target].clone(),
1162 CLang::Cxx => self.cxx[&target].clone(),
11681163 };
11691164
11701165 // Filter out -O and /O (the optimization flags) that we picked up
......@@ -1217,7 +1212,7 @@ impl Build {
12171212 if self.config.dry_run() {
12181213 return None;
12191214 }
1220 self.ar.borrow().get(&target).cloned()
1215 self.ar.get(&target).cloned()
12211216 }
12221217
12231218 /// Returns the path to the `ranlib` utility for the target specified.
......@@ -1225,7 +1220,7 @@ impl Build {
12251220 if self.config.dry_run() {
12261221 return None;
12271222 }
1228 self.ranlib.borrow().get(&target).cloned()
1223 self.ranlib.get(&target).cloned()
12291224 }
12301225
12311226 /// Returns the path to the C++ compiler for the target specified.
......@@ -1233,7 +1228,7 @@ impl Build {
12331228 if self.config.dry_run() {
12341229 return Ok(PathBuf::new());
12351230 }
1236 match self.cxx.borrow().get(&target) {
1231 match self.cxx.get(&target) {
12371232 Some(p) => Ok(p.path().into()),
12381233 None => Err(format!("target `{target}` is not configured as a host, only as a target")),
12391234 }
......@@ -1250,7 +1245,7 @@ impl Build {
12501245 } else if target.contains("vxworks") {
12511246 // need to use CXX compiler as linker to resolve the exception functions
12521247 // that are only existed in CXX libraries
1253 Some(self.cxx.borrow()[&target].path().into())
1248 Some(self.cxx[&target].path().into())
12541249 } else if !self.config.is_host_target(target)
12551250 && helpers::use_host_linker(target)
12561251 && !target.is_msvc()
src/bootstrap/src/utils/build_stamp/tests.rs+10-14
......@@ -1,32 +1,28 @@
11use std::path::PathBuf;
22
3use crate::{BuildStamp, Config, Flags};
3use tempfile::TempDir;
44
5fn temp_dir() -> PathBuf {
6 let config =
7 Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()]));
8 config.tempdir()
9}
5use crate::{BuildStamp, Config, Flags};
106
117#[test]
128#[should_panic(expected = "prefix can not start or end with '.'")]
139fn test_with_invalid_prefix() {
14 let dir = temp_dir();
15 BuildStamp::new(&dir).with_prefix(".invalid");
10 let dir = TempDir::new().unwrap();
11 BuildStamp::new(dir.path()).with_prefix(".invalid");
1612}
1713
1814#[test]
1915#[should_panic(expected = "prefix can not start or end with '.'")]
2016fn test_with_invalid_prefix2() {
21 let dir = temp_dir();
22 BuildStamp::new(&dir).with_prefix("invalid.");
17 let dir = TempDir::new().unwrap();
18 BuildStamp::new(dir.path()).with_prefix("invalid.");
2319}
2420
2521#[test]
2622fn test_is_up_to_date() {
27 let dir = temp_dir();
23 let dir = TempDir::new().unwrap();
2824
29 let mut build_stamp = BuildStamp::new(&dir).add_stamp("v1.0.0");
25 let mut build_stamp = BuildStamp::new(dir.path()).add_stamp("v1.0.0");
3026 build_stamp.write().unwrap();
3127
3228 assert!(
......@@ -45,9 +41,9 @@ fn test_is_up_to_date() {
4541
4642#[test]
4743fn test_with_prefix() {
48 let dir = temp_dir();
44 let dir = TempDir::new().unwrap();
4945
50 let stamp = BuildStamp::new(&dir).add_stamp("v1.0.0");
46 let stamp = BuildStamp::new(dir.path()).add_stamp("v1.0.0");
5147 assert_eq!(stamp.path.file_name().unwrap(), ".stamp");
5248
5349 let stamp = stamp.with_prefix("test");
src/bootstrap/src/utils/cache.rs+29-10
......@@ -17,6 +17,7 @@ use std::borrow::Borrow;
1717use std::cell::RefCell;
1818use std::cmp::Ordering;
1919use std::collections::HashMap;
20use std::fmt::Debug;
2021use std::hash::{Hash, Hasher};
2122use std::marker::PhantomData;
2223use std::ops::Deref;
......@@ -208,25 +209,30 @@ pub static INTERNER: LazyLock<Interner> = LazyLock::new(Interner::default);
208209/// any type in its output. It is a write-once cache; values are never evicted,
209210/// which means that references to the value can safely be returned from the
210211/// `get()` method.
211#[derive(Debug)]
212pub struct Cache(
213 RefCell<
212#[derive(Debug, Default)]
213pub struct Cache {
214 cache: RefCell<
214215 HashMap<
215216 TypeId,
216217 Box<dyn Any>, // actually a HashMap<Step, Interned<Step::Output>>
217218 >,
218219 >,
219);
220 #[cfg(test)]
221 /// Contains steps in the same order in which they were executed
222 /// Useful for tests
223 /// Tuples (step, step output)
224 executed_steps: RefCell<Vec<(Box<dyn Any>, Box<dyn Any>)>>,
225}
220226
221227impl Cache {
222228 /// Creates a new empty cache.
223229 pub fn new() -> Cache {
224 Cache(RefCell::new(HashMap::new()))
230 Cache::default()
225231 }
226232
227233 /// Stores the result of a computation step in the cache.
228234 pub fn put<S: Step>(&self, step: S, value: S::Output) {
229 let mut cache = self.0.borrow_mut();
235 let mut cache = self.cache.borrow_mut();
230236 let type_id = TypeId::of::<S>();
231237 let stepcache = cache
232238 .entry(type_id)
......@@ -234,12 +240,20 @@ impl Cache {
234240 .downcast_mut::<HashMap<S, S::Output>>()
235241 .expect("invalid type mapped");
236242 assert!(!stepcache.contains_key(&step), "processing {step:?} a second time");
243
244 #[cfg(test)]
245 {
246 let step: Box<dyn Any> = Box::new(step.clone());
247 let output: Box<dyn Any> = Box::new(value.clone());
248 self.executed_steps.borrow_mut().push((step, output));
249 }
250
237251 stepcache.insert(step, value);
238252 }
239253
240254 /// Retrieves a cached result for the given step, if available.
241255 pub fn get<S: Step>(&self, step: &S) -> Option<S::Output> {
242 let mut cache = self.0.borrow_mut();
256 let mut cache = self.cache.borrow_mut();
243257 let type_id = TypeId::of::<S>();
244258 let stepcache = cache
245259 .entry(type_id)
......@@ -252,8 +266,8 @@ impl Cache {
252266
253267#[cfg(test)]
254268impl Cache {
255 pub fn all<S: Ord + Clone + Step>(&mut self) -> Vec<(S, S::Output)> {
256 let cache = self.0.get_mut();
269 pub fn all<S: Ord + Step>(&mut self) -> Vec<(S, S::Output)> {
270 let cache = self.cache.get_mut();
257271 let type_id = TypeId::of::<S>();
258272 let mut v = cache
259273 .remove(&type_id)
......@@ -265,7 +279,12 @@ impl Cache {
265279 }
266280
267281 pub fn contains<S: Step>(&self) -> bool {
268 self.0.borrow().contains_key(&TypeId::of::<S>())
282 self.cache.borrow().contains_key(&TypeId::of::<S>())
283 }
284
285 #[cfg(test)]
286 pub fn into_executed_steps(mut self) -> Vec<(Box<dyn Any>, Box<dyn Any>)> {
287 mem::take(&mut self.executed_steps.borrow_mut())
269288 }
270289}
271290
src/bootstrap/src/utils/cc_detect.rs+12-9
......@@ -61,8 +61,8 @@ fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build {
6161///
6262/// This function determines which targets need a C compiler (and, if needed, a C++ compiler)
6363/// by combining the primary build target, host targets, and any additional targets. For
64/// each target, it calls [`find_target`] to configure the necessary compiler tools.
65pub fn find(build: &Build) {
64/// each target, it calls [`fill_target_compiler`] to configure the necessary compiler tools.
65pub fn fill_compilers(build: &mut Build) {
6666 let targets: HashSet<_> = match build.config.cmd {
6767 // We don't need to check cross targets for these commands.
6868 crate::Subcommand::Clean { .. }
......@@ -87,7 +87,7 @@ pub fn find(build: &Build) {
8787 };
8888
8989 for target in targets.into_iter() {
90 find_target(build, target);
90 fill_target_compiler(build, target);
9191 }
9292}
9393
......@@ -96,7 +96,7 @@ pub fn find(build: &Build) {
9696/// This function uses both user-specified configuration (from `bootstrap.toml`) and auto-detection
9797/// logic to determine the correct C/C++ compilers for the target. It also determines the appropriate
9898/// archiver (`ar`) and sets up additional compilation flags (both handled and unhandled).
99pub fn find_target(build: &Build, target: TargetSelection) {
99pub fn fill_target_compiler(build: &mut Build, target: TargetSelection) {
100100 let mut cfg = new_cc_build(build, target);
101101 let config = build.config.target_config.get(&target);
102102 if let Some(cc) = config
......@@ -113,7 +113,7 @@ pub fn find_target(build: &Build, target: TargetSelection) {
113113 cfg.try_get_archiver().map(|c| PathBuf::from(c.get_program())).ok()
114114 };
115115
116 build.cc.borrow_mut().insert(target, compiler.clone());
116 build.cc.insert(target, compiler.clone());
117117 let mut cflags = build.cc_handled_clags(target, CLang::C);
118118 cflags.extend(build.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::C));
119119
......@@ -135,7 +135,7 @@ pub fn find_target(build: &Build, target: TargetSelection) {
135135 // for VxWorks, record CXX compiler which will be used in lib.rs:linker()
136136 if cxx_configured || target.contains("vxworks") {
137137 let compiler = cfg.get_compiler();
138 build.cxx.borrow_mut().insert(target, compiler);
138 build.cxx.insert(target, compiler);
139139 }
140140
141141 build.verbose(|| println!("CC_{} = {:?}", target.triple, build.cc(target)));
......@@ -148,11 +148,11 @@ pub fn find_target(build: &Build, target: TargetSelection) {
148148 }
149149 if let Some(ar) = ar {
150150 build.verbose(|| println!("AR_{} = {ar:?}", target.triple));
151 build.ar.borrow_mut().insert(target, ar);
151 build.ar.insert(target, ar);
152152 }
153153
154154 if let Some(ranlib) = config.and_then(|c| c.ranlib.clone()) {
155 build.ranlib.borrow_mut().insert(target, ranlib);
155 build.ranlib.insert(target, ranlib);
156156 }
157157}
158158
......@@ -221,7 +221,10 @@ fn default_compiler(
221221 }
222222
223223 t if t.contains("-wasi") => {
224 let root = PathBuf::from(std::env::var_os("WASI_SDK_PATH")?);
224 let root = build
225 .wasi_sdk_path
226 .as_ref()
227 .expect("WASI_SDK_PATH mut be configured for a -wasi target");
225228 let compiler = match compiler {
226229 Language::C => format!("{t}-clang"),
227230 Language::CPlusPlus => format!("{t}-clang++"),
src/bootstrap/src/utils/cc_detect/tests.rs+14-22
......@@ -77,11 +77,11 @@ fn test_new_cc_build() {
7777
7878#[test]
7979fn test_default_compiler_wasi() {
80 let build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
80 let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
8181 let target = TargetSelection::from_user("wasm32-wasi");
8282 let wasi_sdk = PathBuf::from("/wasi-sdk");
83 // SAFETY: bootstrap tests run on a single thread
84 unsafe { env::set_var("WASI_SDK_PATH", &wasi_sdk) };
83 build.wasi_sdk_path = Some(wasi_sdk.clone());
84
8585 let mut cfg = cc::Build::new();
8686 if let Some(result) = default_compiler(&mut cfg, Language::C, target.clone(), &build) {
8787 let expected = {
......@@ -94,10 +94,6 @@ fn test_default_compiler_wasi() {
9494 "default_compiler should return a compiler path for wasi target when WASI_SDK_PATH is set"
9595 );
9696 }
97 // SAFETY: bootstrap tests run on a single thread
98 unsafe {
99 env::remove_var("WASI_SDK_PATH");
100 }
10197}
10298
10399#[test]
......@@ -119,18 +115,14 @@ fn test_find_target_with_config() {
119115 target_config.ar = Some(PathBuf::from("dummy-ar"));
120116 target_config.ranlib = Some(PathBuf::from("dummy-ranlib"));
121117 build.config.target_config.insert(target.clone(), target_config);
122 find_target(&build, target.clone());
123 let binding = build.cc.borrow();
124 let cc_tool = binding.get(&target).unwrap();
118 fill_target_compiler(&mut build, target.clone());
119 let cc_tool = build.cc.get(&target).unwrap();
125120 assert_eq!(cc_tool.path(), &PathBuf::from("dummy-cc"));
126 let binding = build.cxx.borrow();
127 let cxx_tool = binding.get(&target).unwrap();
121 let cxx_tool = build.cxx.get(&target).unwrap();
128122 assert_eq!(cxx_tool.path(), &PathBuf::from("dummy-cxx"));
129 let binding = build.ar.borrow();
130 let ar = binding.get(&target).unwrap();
123 let ar = build.ar.get(&target).unwrap();
131124 assert_eq!(ar, &PathBuf::from("dummy-ar"));
132 let binding = build.ranlib.borrow();
133 let ranlib = binding.get(&target).unwrap();
125 let ranlib = build.ranlib.get(&target).unwrap();
134126 assert_eq!(ranlib, &PathBuf::from("dummy-ranlib"));
135127}
136128
......@@ -139,12 +131,12 @@ fn test_find_target_without_config() {
139131 let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
140132 let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
141133 build.config.target_config.clear();
142 find_target(&build, target.clone());
143 assert!(build.cc.borrow().contains_key(&target));
134 fill_target_compiler(&mut build, target.clone());
135 assert!(build.cc.contains_key(&target));
144136 if !target.triple.contains("vxworks") {
145 assert!(build.cxx.borrow().contains_key(&target));
137 assert!(build.cxx.contains_key(&target));
146138 }
147 assert!(build.ar.borrow().contains_key(&target));
139 assert!(build.ar.contains_key(&target));
148140}
149141
150142#[test]
......@@ -154,8 +146,8 @@ fn test_find() {
154146 let target2 = TargetSelection::from_user("x86_64-unknown-openbsd");
155147 build.targets.push(target1.clone());
156148 build.hosts.push(target2.clone());
157 find(&build);
149 fill_compilers(&mut build);
158150 for t in build.hosts.iter().chain(build.targets.iter()).chain(iter::once(&build.host_target)) {
159 assert!(build.cc.borrow().contains_key(t), "CC not set for target {}", t.triple);
151 assert!(build.cc.contains_key(t), "CC not set for target {}", t.triple);
160152 }
161153}
src/bootstrap/src/utils/helpers.rs+1-1
......@@ -98,7 +98,7 @@ pub fn is_dylib(path: &Path) -> bool {
9898
9999/// Return the path to the containing submodule if available.
100100pub fn submodule_path_of(builder: &Builder<'_>, path: &str) -> Option<String> {
101 let submodule_paths = build_helper::util::parse_gitmodules(&builder.src);
101 let submodule_paths = builder.submodule_paths();
102102 submodule_paths.iter().find_map(|submodule_path| {
103103 if path.starts_with(submodule_path) { Some(submodule_path.to_string()) } else { None }
104104 })
src/bootstrap/src/utils/shared_helpers.rs+3
......@@ -6,6 +6,9 @@
66
77#![allow(dead_code)]
88
9#[cfg(test)]
10mod tests;
11
912use std::env;
1013use std::ffi::OsString;
1114use std::fs::OpenOptions;
src/bootstrap/src/utils/shared_helpers/tests.rs created+28
......@@ -0,0 +1,28 @@
1use crate::utils::shared_helpers::parse_value_from_args;
2
3#[test]
4fn test_parse_value_from_args() {
5 let args = vec![
6 "--stage".into(),
7 "1".into(),
8 "--version".into(),
9 "2".into(),
10 "--target".into(),
11 "x86_64-unknown-linux".into(),
12 ];
13
14 assert_eq!(parse_value_from_args(args.as_slice(), "--stage").unwrap(), "1");
15 assert_eq!(parse_value_from_args(args.as_slice(), "--version").unwrap(), "2");
16 assert_eq!(parse_value_from_args(args.as_slice(), "--target").unwrap(), "x86_64-unknown-linux");
17 assert!(parse_value_from_args(args.as_slice(), "random-key").is_none());
18
19 let args = vec![
20 "app-name".into(),
21 "--key".into(),
22 "value".into(),
23 "random-value".into(),
24 "--sysroot=/x/y/z".into(),
25 ];
26 assert_eq!(parse_value_from_args(args.as_slice(), "--key").unwrap(), "value");
27 assert_eq!(parse_value_from_args(args.as_slice(), "--sysroot").unwrap(), "/x/y/z");
28}
src/bootstrap/src/utils/tests/mod.rs+2-1
......@@ -1,2 +1,3 @@
1//! This module contains shared utilities for bootstrap tests.
2
13pub mod git;
2mod shared_helpers_tests;
src/bootstrap/src/utils/tests/shared_helpers_tests.rs deleted-35
......@@ -1,35 +0,0 @@
1//! The `shared_helpers` module can't have its own tests submodule, because
2//! that would cause problems for the shim binaries that include it via
3//! `#[path]`, so instead those unit tests live here.
4//!
5//! To prevent tidy from complaining about this file not being named `tests.rs`,
6//! it lives inside a submodule directory named `tests`.
7
8use crate::utils::shared_helpers::parse_value_from_args;
9
10#[test]
11fn test_parse_value_from_args() {
12 let args = vec![
13 "--stage".into(),
14 "1".into(),
15 "--version".into(),
16 "2".into(),
17 "--target".into(),
18 "x86_64-unknown-linux".into(),
19 ];
20
21 assert_eq!(parse_value_from_args(args.as_slice(), "--stage").unwrap(), "1");
22 assert_eq!(parse_value_from_args(args.as_slice(), "--version").unwrap(), "2");
23 assert_eq!(parse_value_from_args(args.as_slice(), "--target").unwrap(), "x86_64-unknown-linux");
24 assert!(parse_value_from_args(args.as_slice(), "random-key").is_none());
25
26 let args = vec![
27 "app-name".into(),
28 "--key".into(),
29 "value".into(),
30 "random-value".into(),
31 "--sysroot=/x/y/z".into(),
32 ];
33 assert_eq!(parse_value_from_args(args.as_slice(), "--key").unwrap(), "value");
34 assert_eq!(parse_value_from_args(args.as_slice(), "--sysroot").unwrap(), "/x/y/z");
35}
src/build_helper/src/util.rs+10-16
......@@ -2,7 +2,6 @@ use std::fs::File;
22use std::io::{BufRead, BufReader};
33use std::path::Path;
44use std::process::Command;
5use std::sync::OnceLock;
65
76/// Invokes `build_helper::util::detail_exit` with `cfg!(test)`
87///
......@@ -51,25 +50,20 @@ pub fn try_run(cmd: &mut Command, print_cmd_on_fail: bool) -> Result<(), ()> {
5150}
5251
5352/// Returns the submodule paths from the `.gitmodules` file in the given directory.
54pub fn parse_gitmodules(target_dir: &Path) -> &[String] {
55 static SUBMODULES_PATHS: OnceLock<Vec<String>> = OnceLock::new();
53pub fn parse_gitmodules(target_dir: &Path) -> Vec<String> {
5654 let gitmodules = target_dir.join(".gitmodules");
5755 assert!(gitmodules.exists(), "'{}' file is missing.", gitmodules.display());
5856
59 let init_submodules_paths = || {
60 let file = File::open(gitmodules).unwrap();
57 let file = File::open(gitmodules).unwrap();
6158
62 let mut submodules_paths = vec![];
63 for line in BufReader::new(file).lines().map_while(Result::ok) {
64 let line = line.trim();
65 if line.starts_with("path") {
66 let actual_path = line.split(' ').last().expect("Couldn't get value of path");
67 submodules_paths.push(actual_path.to_owned());
68 }
59 let mut submodules_paths = vec![];
60 for line in BufReader::new(file).lines().map_while(Result::ok) {
61 let line = line.trim();
62 if line.starts_with("path") {
63 let actual_path = line.split(' ').last().expect("Couldn't get value of path");
64 submodules_paths.push(actual_path.to_owned());
6965 }
66 }
7067
71 submodules_paths
72 };
73
74 SUBMODULES_PATHS.get_or_init(|| init_submodules_paths())
68 submodules_paths
7569}
src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile-4
......@@ -22,10 +22,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
2222COPY scripts/sccache.sh /scripts/
2323RUN sh /scripts/sccache.sh
2424
25RUN mkdir -p /config
26RUN echo "[rust]" > /config/nopt-std-config.toml
27RUN echo "optimize = false" >> /config/nopt-std-config.toml
28
2925ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests
3026ARG SCRIPT_ARG
3127COPY scripts/stage_2_test_set1.sh /scripts/
src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile+1-5
......@@ -22,12 +22,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
2222COPY scripts/sccache.sh /scripts/
2323RUN sh /scripts/sccache.sh
2424
25RUN mkdir -p /config
26RUN echo "[rust]" > /config/nopt-std-config.toml
27RUN echo "optimize = false" >> /config/nopt-std-config.toml
28
2925ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu \
3026 --disable-optimize-tests \
3127 --set rust.test-compare-mode
32ENV SCRIPT python3 ../x.py test --stage 1 --config /config/nopt-std-config.toml library/std \
28ENV SCRIPT python3 ../x.py test --stage 1 --set rust.optimize=false library/std \
3329 && python3 ../x.py --stage 2 test
src/ci/github-actions/jobs.yml+1-1
......@@ -300,7 +300,7 @@ auto:
300300 env:
301301 IMAGE: i686-gnu-nopt
302302 DOCKER_SCRIPT: >-
303 python3 ../x.py test --stage 1 --config /config/nopt-std-config.toml library/std &&
303 python3 ../x.py test --stage 1 --set rust.optimize=false library/std &&
304304 /scripts/stage_2_test_set2.sh
305305 <<: *job-linux-4c
306306
src/tools/miri/src/lib.rs+1-1
......@@ -16,7 +16,7 @@
1616#![feature(unqualified_local_imports)]
1717#![feature(derive_coerce_pointee)]
1818#![feature(arbitrary_self_types)]
19#![feature(file_lock)]
19#![cfg_attr(bootstrap, feature(file_lock))]
2020// Configure clippy and other lints
2121#![allow(
2222 clippy::collapsible_else_if,
src/tools/miri/tests/pass/shims/fs.rs-1
......@@ -2,7 +2,6 @@
22
33#![feature(io_error_more)]
44#![feature(io_error_uncategorized)]
5#![feature(file_lock)]
65
76use std::collections::BTreeMap;
87use std::ffi::OsString;
src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs+1-1
......@@ -486,7 +486,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
486486 rustc_legacy_const_generics, Normal, template!(List: "N"), ErrorFollowing,
487487 INTERNAL_UNSTABLE
488488 ),
489 // Do not const-check this function's body. It will always get replaced during CTFE.
489 // Do not const-check this function's body. It will always get replaced during CTFE via `hook_special_const_fn`.
490490 rustc_attr!(
491491 rustc_do_not_const_check, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE
492492 ),
tests/ui-fulldeps/stable-mir/check_variant.rs created+183
......@@ -0,0 +1,183 @@
1//@ run-pass
2//! Test that users are able to use stable mir APIs to retrieve
3//! discriminant value and type for AdtDef and Coroutine variants
4
5//@ ignore-stage1
6//@ ignore-cross-compile
7//@ ignore-remote
8//@ edition: 2024
9
10#![feature(rustc_private)]
11#![feature(assert_matches)]
12
13extern crate rustc_middle;
14#[macro_use]
15extern crate rustc_smir;
16extern crate rustc_driver;
17extern crate rustc_interface;
18extern crate stable_mir;
19
20use std::io::Write;
21use std::ops::ControlFlow;
22
23use stable_mir::CrateItem;
24use stable_mir::crate_def::CrateDef;
25use stable_mir::mir::{AggregateKind, Rvalue, Statement, StatementKind};
26use stable_mir::ty::{IntTy, RigidTy, Ty};
27
28const CRATE_NAME: &str = "crate_variant_ty";
29
30/// Test if we can retrieve discriminant info for different types.
31fn test_def_tys() -> ControlFlow<()> {
32 check_adt_mono();
33 check_adt_poly();
34 check_adt_poly2();
35
36 ControlFlow::Continue(())
37}
38
39fn check_adt_mono() {
40 let mono = get_fn("mono").expect_body();
41
42 check_statement_is_aggregate_assign(
43 &mono.blocks[0].statements[0],
44 0,
45 RigidTy::Int(IntTy::Isize),
46 );
47 check_statement_is_aggregate_assign(
48 &mono.blocks[1].statements[0],
49 1,
50 RigidTy::Int(IntTy::Isize),
51 );
52 check_statement_is_aggregate_assign(
53 &mono.blocks[2].statements[0],
54 2,
55 RigidTy::Int(IntTy::Isize),
56 );
57}
58
59fn check_adt_poly() {
60 let poly = get_fn("poly").expect_body();
61
62 check_statement_is_aggregate_assign(
63 &poly.blocks[0].statements[0],
64 0,
65 RigidTy::Int(IntTy::Isize),
66 );
67 check_statement_is_aggregate_assign(
68 &poly.blocks[1].statements[0],
69 1,
70 RigidTy::Int(IntTy::Isize),
71 );
72 check_statement_is_aggregate_assign(
73 &poly.blocks[2].statements[0],
74 2,
75 RigidTy::Int(IntTy::Isize),
76 );
77}
78
79fn check_adt_poly2() {
80 let poly = get_fn("poly2").expect_body();
81
82 check_statement_is_aggregate_assign(
83 &poly.blocks[0].statements[0],
84 0,
85 RigidTy::Int(IntTy::Isize),
86 );
87 check_statement_is_aggregate_assign(
88 &poly.blocks[1].statements[0],
89 1,
90 RigidTy::Int(IntTy::Isize),
91 );
92 check_statement_is_aggregate_assign(
93 &poly.blocks[2].statements[0],
94 2,
95 RigidTy::Int(IntTy::Isize),
96 );
97}
98
99fn get_fn(name: &str) -> CrateItem {
100 stable_mir::all_local_items().into_iter().find(|it| it.name().eq(name)).unwrap()
101}
102
103fn check_statement_is_aggregate_assign(
104 statement: &Statement,
105 expected_discr_val: u128,
106 expected_discr_ty: RigidTy,
107) {
108 if let Statement { kind: StatementKind::Assign(_, rvalue), .. } = statement
109 && let Rvalue::Aggregate(aggregate, _) = rvalue
110 && let AggregateKind::Adt(adt_def, variant_idx, ..) = aggregate
111 {
112 let discr = adt_def.discriminant_for_variant(*variant_idx);
113
114 assert_eq!(discr.val, expected_discr_val);
115 assert_eq!(discr.ty, Ty::from_rigid_kind(expected_discr_ty));
116 } else {
117 unreachable!("Unexpected statement");
118 }
119}
120
121/// This test will generate and analyze a dummy crate using the stable mir.
122/// For that, it will first write the dummy crate into a file.
123/// Then it will create a `StableMir` using custom arguments and then
124/// it will run the compiler.
125fn main() {
126 let path = "defs_ty_input.rs";
127 generate_input(&path).unwrap();
128 let args = &[
129 "rustc".to_string(),
130 "-Cpanic=abort".to_string(),
131 "--crate-name".to_string(),
132 CRATE_NAME.to_string(),
133 path.to_string(),
134 ];
135 run!(args, test_def_tys).unwrap();
136}
137
138fn generate_input(path: &str) -> std::io::Result<()> {
139 let mut file = std::fs::File::create(path)?;
140 write!(
141 file,
142 r#"
143 use std::hint::black_box;
144
145 enum Mono {{
146 A,
147 B(i32),
148 C {{ a: i32, b: u32 }},
149 }}
150
151 enum Poly<T> {{
152 A,
153 B(T),
154 C {{ t: T }},
155 }}
156
157 pub fn main() {{
158 mono();
159 poly();
160 poly2::<i32>(1);
161 }}
162
163 fn mono() {{
164 black_box(Mono::A);
165 black_box(Mono::B(6));
166 black_box(Mono::C {{a: 1, b: 10 }});
167 }}
168
169 fn poly() {{
170 black_box(Poly::<i32>::A);
171 black_box(Poly::B(1i32));
172 black_box(Poly::C {{ t: 1i32 }});
173 }}
174
175 fn poly2<T: Copy>(t: T) {{
176 black_box(Poly::<T>::A);
177 black_box(Poly::B(t));
178 black_box(Poly::C {{ t: t }});
179 }}
180 "#
181 )?;
182 Ok(())
183}