diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index dc2eb17589c66..f62b8d1d57620 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1741,7 +1741,7 @@ fn deny_equality_constraints( .map(|segment| segment.ident.name) .zip(poly.trait_ref.path.segments.iter().map(|segment| segment.ident.name)) .all(|(a, b)| a == b) - && let Some(potential_assoc) = full_path.segments.iter().last() + && let Some(potential_assoc) = full_path.segments.last() { suggest(poly, potential_assoc, predicate); } diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index af71db6948329..0b3151fd8b82d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -518,7 +518,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .with_span_help( self.get_closure_bound_clause_span(*def_id), "`Fn` and `FnMut` closures require captured values to be able to be \ - consumed multiple times, but an `FnOnce` consume them only once", + consumed multiple times, but `FnOnce` closures may consume them only once", ) } _ => { diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 98be37fd84b59..e579370ce4ef6 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1113,7 +1113,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { .map(|snippet| { debug_assert!( !(sp.is_empty() && snippet.is_empty()), - "Span must not be empty and have no suggestion" + "Span `{sp:?}` must not be empty and have no suggestion" ); Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] } }) diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 060799e981d46..c310b99d53513 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -146,7 +146,7 @@ impl<'ty> FieldInnerTy<'ty> { }; let path = &ty_path.path; - let ty = path.segments.iter().last().unwrap(); + let ty = path.segments.last().unwrap(); let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments else { panic!("expected bracketed generic arguments"); }; diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 3a21eea3d0aee..9e0075c21b9ed 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -862,8 +862,8 @@ parse_too_many_hashes = too many `#` symbols: raw strings may be delimited by up parse_too_short_hex_escape = numeric character escape is too short -parse_trailing_vert_not_allowed = a trailing `|` is not allowed in an or-pattern - .suggestion = remove the `{$token}` +parse_trailing_vert_not_allowed = a trailing `{$token}` is not allowed in an or-pattern +parse_trailing_vert_not_allowed_suggestion = remove the `{$token}` parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto` parse_trait_alias_cannot_be_const = trait aliases cannot be `const` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index a07d0606fd0f4..a105dd1909e93 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2661,7 +2661,7 @@ pub(crate) enum TopLevelOrPatternNotAllowedSugg { parse_sugg_remove_leading_vert_in_pattern, code = "", applicability = "machine-applicable", - style = "verbose" + style = "tool-only" )] RemoveLeadingVert { #[primary_span] @@ -2694,12 +2694,25 @@ pub(crate) struct UnexpectedVertVertInPattern { pub start: Option, } +#[derive(Subdiagnostic)] +#[suggestion( + parse_trailing_vert_not_allowed, + code = "", + applicability = "machine-applicable", + style = "tool-only" +)] +pub(crate) struct TrailingVertSuggestion { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_trailing_vert_not_allowed)] pub(crate) struct TrailingVertNotAllowed { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] pub span: Span, + #[subdiagnostic] + pub suggestion: TrailingVertSuggestion, #[label(parse_label_while_parsing_or_pattern_here)] pub start: Option, pub token: Token, diff --git a/compiler/rustc_parse/src/parser/cfg_select.rs b/compiler/rustc_parse/src/parser/cfg_select.rs index 2c6fb224d70d6..08a71db4de853 100644 --- a/compiler/rustc_parse/src/parser/cfg_select.rs +++ b/compiler/rustc_parse/src/parser/cfg_select.rs @@ -1,11 +1,12 @@ use rustc_ast::token::Token; use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::util::classify; use rustc_ast::{MetaItemInner, token}; use rustc_errors::PResult; use rustc_span::Span; use crate::exp; -use crate::parser::Parser; +use crate::parser::{AttrWrapper, ForceCollect, Parser, Restrictions, Trailing, UsePreAttrPos}; pub enum CfgSelectPredicate { Cfg(MetaItemInner), @@ -23,19 +24,26 @@ pub struct CfgSelectBranches { pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>, } -/// Parses a `TokenTree` that must be of the form `{ /* ... */ }`, and returns a `TokenStream` where -/// the surrounding braces are stripped. +/// Parses a `TokenTree` consisting either of `{ /* ... */ }` (and strip the braces) or an +/// expression followed by a comma (and strip the comma). fn parse_token_tree<'a>(p: &mut Parser<'a>) -> PResult<'a, TokenStream> { - // Generate an error if the `=>` is not followed by `{`. - if p.token != token::OpenBrace { - p.expect(exp!(OpenBrace))?; + if p.token == token::OpenBrace { + // Strip the outer '{' and '}'. + match p.parse_token_tree() { + TokenTree::Token(..) => unreachable!("because of the expect above"), + TokenTree::Delimited(.., tts) => return Ok(tts), + } } - - // Strip the outer '{' and '}'. - match p.parse_token_tree() { - TokenTree::Token(..) => unreachable!("because of the expect above"), - TokenTree::Delimited(.., tts) => Ok(tts), + let expr = p.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |p, _| { + p.parse_expr_res(Restrictions::STMT_EXPR, AttrWrapper::empty()) + .map(|(expr, _)| (expr, Trailing::No, UsePreAttrPos::No)) + })?; + if !classify::expr_is_complete(&expr) && p.token != token::CloseBrace && p.token != token::Eof { + p.expect(exp!(Comma))?; + } else { + let _ = p.eat(exp!(Comma)); } + Ok(TokenStream::from_ast(&expr)) } pub fn parse_cfg_select<'a>(p: &mut Parser<'a>) -> PResult<'a, CfgSelectBranches> { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index a415849b91517..9754691a0b9ca 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -24,10 +24,10 @@ use crate::errors::{ GenericArgsInPatRequireTurbofishSyntax, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, ParenRangeSuggestion, PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, - TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, UnexpectedExpressionInPattern, - UnexpectedExpressionInPatternSugg, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, - UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, - UnexpectedVertVertInPattern, WrapInParens, + TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, TrailingVertSuggestion, + UnexpectedExpressionInPattern, UnexpectedExpressionInPatternSugg, UnexpectedLifetimeInPattern, + UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, + UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens, }; use crate::parser::expr::{DestructuredFloat, could_be_unclosed_char_literal}; use crate::{exp, maybe_recover_from_interpolated_ty_qpath}; @@ -267,10 +267,9 @@ impl<'a> Parser<'a> { if let PatKind::Or(pats) = &pat.kind { let span = pat.span; - let sub = if pats.len() == 1 { - Some(TopLevelOrPatternNotAllowedSugg::RemoveLeadingVert { - span: span.with_hi(span.lo() + BytePos(1)), - }) + let sub = if let [_] = &pats[..] { + let span = span.with_hi(span.lo() + BytePos(1)); + Some(TopLevelOrPatternNotAllowedSugg::RemoveLeadingVert { span }) } else { Some(TopLevelOrPatternNotAllowedSugg::WrapInParens { span, @@ -362,6 +361,9 @@ impl<'a> Parser<'a> { self.dcx().emit_err(TrailingVertNotAllowed { span: self.token.span, start: lo, + suggestion: TrailingVertSuggestion { + span: self.prev_token.span.shrink_to_hi().with_hi(self.token.span.hi()), + }, token: self.token, note_double_vert: self.token.kind == token::OrOr, }); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index cc30939f5e96c..ca9c124fca63c 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -16,6 +16,7 @@ #![feature(assert_matches)] #![feature(box_patterns)] #![feature(decl_macro)] +#![feature(default_field_values)] #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(rustc_attrs)] @@ -1075,7 +1076,7 @@ pub struct Resolver<'ra, 'tcx> { /// Assert that we are in speculative resolution mode. assert_speculative: bool, - prelude: Option>, + prelude: Option> = None, extern_prelude: FxIndexMap>, /// N.B., this is used only for better diagnostics, not name resolution itself. @@ -1087,10 +1088,10 @@ pub struct Resolver<'ra, 'tcx> { field_visibility_spans: FxHashMap>, /// All imports known to succeed or fail. - determined_imports: Vec>, + determined_imports: Vec> = Vec::new(), /// All non-determined imports. - indeterminate_imports: Vec>, + indeterminate_imports: Vec> = Vec::new(), // Spans for local variables found during pattern resolution. // Used for suggestions during error reporting. @@ -1141,19 +1142,19 @@ pub struct Resolver<'ra, 'tcx> { /// Maps glob imports to the names of items actually imported. glob_map: FxIndexMap>, - glob_error: Option, - visibilities_for_hashing: Vec<(LocalDefId, Visibility)>, + glob_error: Option = None, + visibilities_for_hashing: Vec<(LocalDefId, Visibility)> = Vec::new(), used_imports: FxHashSet, maybe_unused_trait_imports: FxIndexSet, /// Privacy errors are delayed until the end in order to deduplicate them. - privacy_errors: Vec>, + privacy_errors: Vec> = Vec::new(), /// Ambiguity errors are delayed for deduplication. - ambiguity_errors: Vec>, + ambiguity_errors: Vec> = Vec::new(), /// `use` injections are delayed for better placement and deduplication. - use_injections: Vec>, + use_injections: Vec> = Vec::new(), /// Crate-local macro expanded `macro_export` referred to by a module-relative path. - macro_expanded_macro_export_errors: BTreeSet<(Span, Span)>, + macro_expanded_macro_export_errors: BTreeSet<(Span, Span)> = BTreeSet::new(), arenas: &'ra ResolverArenas<'ra>, dummy_binding: NameBinding<'ra>, @@ -1205,9 +1206,9 @@ pub struct Resolver<'ra, 'tcx> { /// Avoid duplicated errors for "name already defined". name_already_seen: FxHashMap, - potentially_unused_imports: Vec>, + potentially_unused_imports: Vec> = Vec::new(), - potentially_unnecessary_qualifications: Vec>, + potentially_unnecessary_qualifications: Vec> = Vec::new(), /// Table for mapping struct IDs into struct constructor IDs, /// it's not used during normal resolution, only for better error reporting. @@ -1216,7 +1217,7 @@ pub struct Resolver<'ra, 'tcx> { lint_buffer: LintBuffer, - next_node_id: NodeId, + next_node_id: NodeId = CRATE_NODE_ID, node_id_to_def_id: NodeMap>, @@ -1234,17 +1235,17 @@ pub struct Resolver<'ra, 'tcx> { item_generics_num_lifetimes: FxHashMap, delegation_fn_sigs: LocalDefIdMap, - main_def: Option, + main_def: Option = None, trait_impls: FxIndexMap>, /// A list of proc macro LocalDefIds, written out in the order in which /// they are declared in the static array generated by proc_macro_harness. - proc_macros: Vec, + proc_macros: Vec = Vec::new(), confused_type_with_std_module: FxIndexMap, /// Whether lifetime elision was successful. lifetime_elision_allowed: FxHashSet, /// Names of items that were stripped out via cfg with their corresponding cfg meta item. - stripped_cfg_items: Vec>, + stripped_cfg_items: Vec> = Vec::new(), effective_visibilities: EffectiveVisibilities, doc_link_resolutions: FxIndexMap, @@ -1558,9 +1559,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { field_defaults: Default::default(), field_visibility_spans: FxHashMap::default(), - determined_imports: Vec::new(), - indeterminate_imports: Vec::new(), - pat_span_map: Default::default(), partial_res_map: Default::default(), import_res_map: Default::default(), @@ -1579,16 +1577,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ast_transform_scopes: FxHashMap::default(), glob_map: Default::default(), - glob_error: None, - visibilities_for_hashing: Default::default(), used_imports: FxHashSet::default(), maybe_unused_trait_imports: Default::default(), - privacy_errors: Vec::new(), - ambiguity_errors: Vec::new(), - use_injections: Vec::new(), - macro_expanded_macro_export_errors: BTreeSet::new(), - arenas, dummy_binding: arenas.new_pub_res_binding(Res::Err, DUMMY_SP, LocalExpnId::ROOT), builtin_types_bindings: PrimTy::ALL @@ -1632,8 +1623,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { derive_data: Default::default(), local_macro_def_scopes: FxHashMap::default(), name_already_seen: FxHashMap::default(), - potentially_unused_imports: Vec::new(), - potentially_unnecessary_qualifications: Default::default(), struct_constructors: Default::default(), unused_macros: Default::default(), unused_macro_rules: Default::default(), @@ -1643,16 +1632,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { builtin_attrs: Default::default(), containers_deriving_copy: Default::default(), lint_buffer: LintBuffer::default(), - next_node_id: CRATE_NODE_ID, node_id_to_def_id, disambiguator: DisambiguatorState::new(), placeholder_field_indices: Default::default(), invocation_parents, legacy_const_generic_args: Default::default(), item_generics_num_lifetimes: Default::default(), - main_def: Default::default(), trait_impls: Default::default(), - proc_macros: Default::default(), confused_type_with_std_module: Default::default(), lifetime_elision_allowed: Default::default(), stripped_cfg_items: Default::default(), @@ -1667,6 +1653,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { current_crate_outer_attr_insert_span, mods_with_parse_errors: Default::default(), impl_trait_names: Default::default(), + .. }; let root_parent_scope = ParentScope::module(graph_root, resolver.arenas); diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index c59290a757b67..db8b527d59312 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -223,13 +223,14 @@ pub macro assert_matches { /// } /// ``` /// -/// The `cfg_select!` macro can also be used in expression position: +/// The `cfg_select!` macro can also be used in expression position, with or without braces on the +/// right-hand side: /// /// ``` /// #![feature(cfg_select)] /// /// let _some_string = cfg_select! { -/// unix => { "With great power comes great electricity bills" } +/// unix => "With great power comes great electricity bills", /// _ => { "Behind every successful diet is an unwatched pizza" } /// }; /// ``` diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 044d360ac3766..537f4b6184f1b 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -11,21 +11,18 @@ dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "bitflags" version = "2.6.0" @@ -47,6 +44,7 @@ version = "0.0.0" dependencies = [ "build_helper", "cc", + "chrono", "clap", "clap_complete", "cmake", @@ -71,7 +69,6 @@ dependencies = [ "toml", "tracing", "tracing-chrome", - "tracing-forest", "tracing-subscriber", "walkdir", "windows", @@ -112,6 +109,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "num-traits", +] + [[package]] name = "clap" version = "4.5.20" @@ -458,6 +464,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "objc2-core-foundation" version = "0.3.1" @@ -775,26 +790,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "thread_local" version = "1.1.8" @@ -857,19 +852,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-forest" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee40835db14ddd1e3ba414292272eddde9dad04d3d4b65509656414d1c42592f" -dependencies = [ - "ansi_term", - "smallvec", - "thiserror", - "tracing", - "tracing-subscriber", -] - [[package]] name = "tracing-log" version = "0.2.0" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 60d4976b93440..bdf0b42255ee5 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -7,7 +7,7 @@ default-run = "bootstrap" [features] build-metrics = ["sysinfo"] -tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:tracing-forest"] +tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:chrono", "dep:tempfile"] [lib] path = "src/lib.rs" @@ -61,10 +61,11 @@ xz2 = "0.1" sysinfo = { version = "0.37.0", default-features = false, optional = true, features = ["system"] } # Dependencies needed by the `tracing` feature +chrono = { version = "0.4", default-features = false, optional = true, features = ["now", "std"] } tracing = { version = "0.1", optional = true, features = ["attributes"] } tracing-chrome = { version = "0.7", optional = true } tracing-subscriber = { version = "0.3", optional = true, features = ["env-filter", "fmt", "registry", "std"] } -tracing-forest = { version = "0.1.6", optional = true, default-features = false, features = ["smallvec", "ansi", "env-filter"] } +tempfile = { version = "3.15.0", optional = true } [target.'cfg(windows)'.dependencies.junction] version = "1.0.0" diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index cf24fedaebb15..8b2d67266a77b 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -7,6 +7,7 @@ use std::fs::{self, OpenOptions}; use std::io::{self, BufRead, BufReader, IsTerminal, Write}; +use std::path::Path; use std::str::FromStr; use std::time::Instant; use std::{env, process}; @@ -15,19 +16,16 @@ use bootstrap::{ Build, CONFIG_CHANGE_HISTORY, ChangeId, Config, Flags, Subcommand, debug, find_recent_config_change_ids, human_readable_changes, t, }; -#[cfg(feature = "tracing")] -use tracing::instrument; -fn is_bootstrap_profiling_enabled() -> bool { - env::var("BOOTSTRAP_PROFILE").is_ok_and(|v| v == "1") +fn is_tracing_enabled() -> bool { + cfg!(feature = "tracing") } -#[cfg_attr(feature = "tracing", instrument(level = "trace", name = "main"))] fn main() { #[cfg(feature = "tracing")] - let _guard = setup_tracing(); + let guard = bootstrap::setup_tracing("BOOTSTRAP_TRACING"); - let start_time = Instant::now(); + let _start_time = Instant::now(); let args = env::args().skip(1).collect::>(); @@ -102,6 +100,35 @@ fn main() { let dump_bootstrap_shims = config.dump_bootstrap_shims; let out_dir = config.out.clone(); + let tracing_enabled = is_tracing_enabled(); + + // Prepare a directory for tracing output + // Also store a symlink named "latest" to point to the latest tracing directory. + let tracing_dir = out_dir.join("bootstrap-trace").join(std::process::id().to_string()); + let latest_trace_dir = tracing_dir.parent().unwrap().join("latest"); + if tracing_enabled { + let _ = std::fs::remove_dir_all(&tracing_dir); + std::fs::create_dir_all(&tracing_dir).unwrap(); + + #[cfg(windows)] + let _ = std::fs::remove_dir(&latest_trace_dir); + #[cfg(not(windows))] + let _ = std::fs::remove_file(&latest_trace_dir); + + #[cfg(not(windows))] + fn symlink_dir_inner(original: &Path, link: &Path) -> io::Result<()> { + use std::os::unix::fs; + fs::symlink(original, link) + } + + #[cfg(windows)] + fn symlink_dir_inner(target: &Path, junction: &Path) -> io::Result<()> { + junction::create(target, junction) + } + + t!(symlink_dir_inner(&tracing_dir, &latest_trace_dir)); + } + debug!("creating new build based on config"); let mut build = Build::new(config); build.build(); @@ -156,12 +183,13 @@ fn main() { } } - if is_bootstrap_profiling_enabled() { - build.report_summary(start_time); - } - #[cfg(feature = "tracing")] - build.report_step_graph(); + { + build.report_summary(&tracing_dir.join("command-stats.txt"), _start_time); + build.report_step_graph(&tracing_dir); + guard.copy_to_dir(&tracing_dir); + eprintln!("Tracing/profiling output has been written to {}", latest_trace_dir.display()); + } } fn check_version(config: &Config) -> Option { @@ -219,37 +247,3 @@ fn check_version(config: &Config) -> Option { Some(msg) } - -// # Note on `tracing` usage in bootstrap -// -// Due to the conditional compilation via the `tracing` cargo feature, this means that `tracing` -// usages in bootstrap need to be also gated behind the `tracing` feature: -// -// - `tracing` macros with log levels (`trace!`, `debug!`, `warn!`, `info`, `error`) should not be -// used *directly*. You should use the wrapped `tracing` macros which gate the actual invocations -// behind `feature = "tracing"`. -// - `tracing`'s `#[instrument(..)]` macro will need to be gated like `#![cfg_attr(feature = -// "tracing", instrument(..))]`. -#[cfg(feature = "tracing")] -fn setup_tracing() -> impl Drop { - use tracing_forest::ForestLayer; - use tracing_subscriber::EnvFilter; - use tracing_subscriber::layer::SubscriberExt; - - let filter = EnvFilter::from_env("BOOTSTRAP_TRACING"); - - let mut chrome_layer = tracing_chrome::ChromeLayerBuilder::new().include_args(true); - - // Writes the Chrome profile to trace-.json if enabled - if !is_bootstrap_profiling_enabled() { - chrome_layer = chrome_layer.writer(io::sink()); - } - - let (chrome_layer, _guard) = chrome_layer.build(); - - let registry = - tracing_subscriber::registry().with(filter).with(ForestLayer::default()).with(chrome_layer); - - tracing::subscriber::set_global_default(registry).unwrap(); - _guard -} diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 6d393446d458e..1e08e8547dc72 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -172,7 +172,7 @@ impl Rustc { impl Step for Rustc { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -324,7 +324,7 @@ pub struct CodegenBackend { impl Step for CodegenBackend { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -410,7 +410,7 @@ macro_rules! tool_check_step { impl Step for $name { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; /// Most of the tool-checks using this macro are run by default. const DEFAULT: bool = true $( && $default )?; diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 23d9a032eb909..3c4aa0886c2a5 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -256,7 +256,7 @@ impl Rustc { impl Step for Rustc { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 2a236de01921e..d860cafa1c0fd 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -16,7 +16,7 @@ use std::{env, fs, str}; use serde_derive::Deserialize; #[cfg(feature = "tracing")] -use tracing::{instrument, span}; +use tracing::span; use crate::core::build_steps::gcc::{Gcc, GccOutput, add_cg_gcc_cargo_flags}; use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts}; @@ -104,7 +104,6 @@ impl Step for Std { run.crate_or_deps("sysroot").path("library") } - #[cfg_attr(feature = "tracing", instrument(level = "trace", name = "Std::make_run", skip_all))] fn make_run(run: RunConfig<'_>) { let crates = std_crates_for_run_make(&run); let builder = run.builder; @@ -135,19 +134,6 @@ impl Step for Std { /// This will build the standard library for a particular stage of the build /// using the `compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "Std::run", - skip_all, - fields( - target = ?self.target, - compiler = ?self.compiler, - force_recompile = self.force_recompile - ), - ), - )] fn run(self, builder: &Builder<'_>) { let target = self.target; @@ -717,19 +703,6 @@ impl Step for StdLink { /// Note that this assumes that `compiler` has already generated the libstd /// libraries for `target`, and this method will find them in the relevant /// output directory. - #[cfg_attr( - feature = "tracing", - instrument( - level = "trace", - name = "StdLink::run", - skip_all, - fields( - compiler = ?self.compiler, - target_compiler = ?self.target_compiler, - target = ?self.target - ), - ), - )] fn run(self, builder: &Builder<'_>) { let compiler = self.compiler; let target_compiler = self.target_compiler; @@ -895,15 +868,6 @@ impl Step for StartupObjects { /// They don't require any library support as they're just plain old object /// files, so we just use the nightly snapshot compiler to always build them (as /// no other compilers are guaranteed to be available). - #[cfg_attr( - feature = "tracing", - instrument( - level = "trace", - name = "StartupObjects::run", - skip_all, - fields(compiler = ?self.compiler, target = ?self.target), - ), - )] fn run(self, builder: &Builder<'_>) -> Vec<(PathBuf, DependencyType)> { let for_compiler = self.compiler; let target = self.target; @@ -995,7 +959,7 @@ impl Step for Rustc { /// uplifting it from stage Y, causing the other stage to fail when attempting to link with /// stage X which was never actually built. type Output = u32; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; const DEFAULT: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1033,15 +997,6 @@ impl Step for Rustc { /// This will build the compiler for a particular stage of the build using /// the `build_compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "Rustc::run", - skip_all, - fields(previous_compiler = ?self.build_compiler, target = ?self.target), - ), - )] fn run(self, builder: &Builder<'_>) -> u32 { let build_compiler = self.build_compiler; let target = self.target; @@ -1397,6 +1352,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS // Build jemalloc on AArch64 with support for page sizes up to 64K // See: https://github.com/rust-lang/rust/pull/135081 + // See also the "JEMALLOC_SYS_WITH_LG_PAGE" setting in the tool build step. if builder.config.jemalloc(target) && target.starts_with("aarch64") && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() @@ -1517,19 +1473,6 @@ impl Step for RustcLink { } /// Same as `std_link`, only for librustc - #[cfg_attr( - feature = "tracing", - instrument( - level = "trace", - name = "RustcLink::run", - skip_all, - fields( - compiler = ?self.compiler, - previous_stage_compiler = ?self.previous_stage_compiler, - target = ?self.target, - ), - ), - )] fn run(self, builder: &Builder<'_>) { let compiler = self.compiler; let previous_stage_compiler = self.previous_stage_compiler; @@ -1559,7 +1502,7 @@ pub struct GccCodegenBackend { impl Step for GccCodegenBackend { type Output = GccCodegenBackendOutput; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.alias("rustc_codegen_gcc").alias("cg_gcc") @@ -1571,17 +1514,6 @@ impl Step for GccCodegenBackend { }); } - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "GccCodegenBackend::run", - skip_all, - fields( - compilers = ?self.compilers, - ), - ), - )] fn run(self, builder: &Builder<'_>) -> Self::Output { let target = self.compilers.target(); let build_compiler = self.compilers.build_compiler(); @@ -1644,7 +1576,7 @@ pub struct CraneliftCodegenBackend { impl Step for CraneliftCodegenBackend { type Output = BuildStamp; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.alias("rustc_codegen_cranelift").alias("cg_clif") @@ -1656,17 +1588,6 @@ impl Step for CraneliftCodegenBackend { }); } - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "CraneliftCodegenBackend::run", - skip_all, - fields( - compilers = ?self.compilers, - ), - ), - )] fn run(self, builder: &Builder<'_>) -> Self::Output { let target = self.compilers.target(); let build_compiler = self.compilers.build_compiler(); @@ -1841,15 +1762,6 @@ impl Step for Sysroot { /// Returns the sysroot that `compiler` is supposed to use. /// For the stage0 compiler, this is stage0-sysroot (because of the initial std build). /// For all other stages, it's the same stage directory that the compiler lives in. - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "Sysroot::run", - skip_all, - fields(compiler = ?self.compiler), - ), - )] fn run(self, builder: &Builder<'_>) -> PathBuf { let compiler = self.compiler; let host_dir = builder.out.join(compiler.host); @@ -2008,7 +1920,7 @@ pub struct Assemble { impl Step for Assemble { type Output = Compiler; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("compiler/rustc").path("compiler") @@ -2025,15 +1937,6 @@ impl Step for Assemble { /// This will assemble a compiler in `build/$host/stage$stage`. The compiler /// must have been previously produced by the `stage - 1` builder.build /// compiler. - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "Assemble::run", - skip_all, - fields(target_compiler = ?self.target_compiler), - ), - )] fn run(self, builder: &Builder<'_>) -> Compiler { let target_compiler = self.target_compiler; @@ -2592,7 +2495,7 @@ pub fn stream_cargo( let mut cmd = cargo.into_cmd(); #[cfg(feature = "tracing")] - let _run_span = crate::trace_cmd!(cmd); + let _run_span = crate::utils::tracing::trace_cmd(&cmd); // Instruct Cargo to give us json messages on stdout, critically leaving // stderr as piped so we can get those pretty colors. diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 7fdfeabe29d9c..64c2cdd2ec795 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -140,7 +140,7 @@ pub struct RustcDocs { impl Step for RustcDocs { type Output = Option; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; @@ -402,7 +402,7 @@ pub struct Rustc { impl Step for Rustc { type Output = GeneratedTarball; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.alias("rustc") @@ -794,7 +794,7 @@ pub struct RustcDev { impl Step for RustcDev { type Output = Option; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.alias("rustc-dev") @@ -1024,7 +1024,7 @@ impl Step for Src { /// The output path of the src installer tarball type Output = GeneratedTarball; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.alias("rust-src") @@ -1085,7 +1085,7 @@ impl Step for PlainSourceTarball { /// Produces the location of the tarball generated type Output = GeneratedTarball; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; @@ -1233,7 +1233,7 @@ pub struct Cargo { impl Step for Cargo { type Output = Option; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let default = should_build_extended_tool(run.builder, "cargo"); @@ -1287,7 +1287,7 @@ pub struct RustAnalyzer { impl Step for RustAnalyzer { type Output = Option; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let default = should_build_extended_tool(run.builder, "rust-analyzer"); @@ -1330,7 +1330,7 @@ pub struct Clippy { impl Step for Clippy { type Output = Option; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let default = should_build_extended_tool(run.builder, "clippy"); @@ -1378,7 +1378,7 @@ pub struct Miri { impl Step for Miri { type Output = Option; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let default = should_build_extended_tool(run.builder, "miri"); @@ -1428,7 +1428,7 @@ pub struct CraneliftCodegenBackend { impl Step for CraneliftCodegenBackend { type Output = Option; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { // We only want to build the cranelift backend in `x dist` if the backend was enabled @@ -1519,7 +1519,7 @@ pub struct Rustfmt { impl Step for Rustfmt { type Output = Option; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let default = should_build_extended_tool(run.builder, "rustfmt"); @@ -1564,7 +1564,7 @@ pub struct Extended { impl Step for Extended { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; @@ -2301,7 +2301,7 @@ pub struct LlvmTools { impl Step for LlvmTools { type Output = Option; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -2406,7 +2406,7 @@ pub struct LlvmBitcodeLinker { impl Step for LlvmBitcodeLinker { type Output = Option; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let default = should_build_extended_tool(run.builder, "llvm-bitcode-linker"); @@ -2458,7 +2458,7 @@ pub struct RustDev { impl Step for RustDev { type Output = Option; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.alias("rust-dev") @@ -2561,7 +2561,7 @@ pub struct Bootstrap { impl Step for Bootstrap { type Output = Option; const DEFAULT: bool = false; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.alias("bootstrap") @@ -2601,7 +2601,7 @@ pub struct BuildManifest { impl Step for BuildManifest { type Output = GeneratedTarball; const DEFAULT: bool = false; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.alias("build-manifest") @@ -2633,7 +2633,7 @@ pub struct ReproducibleArtifacts { impl Step for ReproducibleArtifacts { type Output = Option; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.alias("reproducible-artifacts") diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 6f0d5203d1176..f6b27d8312063 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -830,7 +830,7 @@ impl Rustc { impl Step for Rustc { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; @@ -975,7 +975,7 @@ macro_rules! tool_doc { impl Step for $tool { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; @@ -1139,7 +1139,7 @@ pub struct ErrorIndex { impl Step for ErrorIndex { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; @@ -1181,7 +1181,7 @@ pub struct UnstableBookGen { impl Step for UnstableBookGen { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; @@ -1248,7 +1248,7 @@ impl RustcBook { impl Step for RustcBook { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index 389afaecea336..2b36b0f2e2717 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -47,7 +47,7 @@ impl GccOutput { impl Step for Gcc { type Output = GccOutput; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/gcc").alias("gcc") diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 666f2715224e2..4457258e9cdd0 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -163,7 +163,7 @@ macro_rules! install { $($name:ident, $condition_name: ident = $path_or_alias: literal, $default_cond:expr, - only_hosts: $only_hosts:expr, + IS_HOST: $IS_HOST:expr, $run_item:block $(, $c:ident)*;)+) => { $( #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -183,7 +183,7 @@ macro_rules! install { impl Step for $name { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = $only_hosts; + const IS_HOST: bool = $IS_HOST; $(const $c: bool = true;)* fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -206,11 +206,11 @@ macro_rules! install { } install!((self, builder, _config), - Docs, path = "src/doc", _config.docs, only_hosts: false, { + Docs, path = "src/doc", _config.docs, IS_HOST: false, { let tarball = builder.ensure(dist::Docs { host: self.target }).expect("missing docs"); install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball); }; - Std, path = "library/std", true, only_hosts: false, { + Std, path = "library/std", true, IS_HOST: false, { // `expect` should be safe, only None when host != build, but this // only runs when host == build let tarball = builder.ensure(dist::Std { @@ -219,13 +219,13 @@ install!((self, builder, _config), }).expect("missing std"); install_sh(builder, "std", self.compiler.stage, Some(self.target), &tarball); }; - Cargo, alias = "cargo", Self::should_build(_config), only_hosts: true, { + Cargo, alias = "cargo", Self::should_build(_config), IS_HOST: true, { let tarball = builder .ensure(dist::Cargo { build_compiler: self.compiler, target: self.target }) .expect("missing cargo"); install_sh(builder, "cargo", self.compiler.stage, Some(self.target), &tarball); }; - RustAnalyzer, alias = "rust-analyzer", Self::should_build(_config), only_hosts: true, { + RustAnalyzer, alias = "rust-analyzer", Self::should_build(_config), IS_HOST: true, { if let Some(tarball) = builder.ensure(dist::RustAnalyzer { build_compiler: self.compiler, target: self.target }) { @@ -236,13 +236,13 @@ install!((self, builder, _config), ); } }; - Clippy, alias = "clippy", Self::should_build(_config), only_hosts: true, { + Clippy, alias = "clippy", Self::should_build(_config), IS_HOST: true, { let tarball = builder .ensure(dist::Clippy { build_compiler: self.compiler, target: self.target }) .expect("missing clippy"); install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball); }; - Miri, alias = "miri", Self::should_build(_config), only_hosts: true, { + Miri, alias = "miri", Self::should_build(_config), IS_HOST: true, { if let Some(tarball) = builder.ensure(dist::Miri { build_compiler: self.compiler, target: self.target }) { install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball); } else { @@ -252,7 +252,7 @@ install!((self, builder, _config), ); } }; - LlvmTools, alias = "llvm-tools", _config.llvm_tools_enabled && _config.llvm_enabled(_config.host_target), only_hosts: true, { + LlvmTools, alias = "llvm-tools", _config.llvm_tools_enabled && _config.llvm_enabled(_config.host_target), IS_HOST: true, { if let Some(tarball) = builder.ensure(dist::LlvmTools { target: self.target }) { install_sh(builder, "llvm-tools", self.compiler.stage, Some(self.target), &tarball); } else { @@ -261,7 +261,7 @@ install!((self, builder, _config), ); } }; - Rustfmt, alias = "rustfmt", Self::should_build(_config), only_hosts: true, { + Rustfmt, alias = "rustfmt", Self::should_build(_config), IS_HOST: true, { if let Some(tarball) = builder.ensure(dist::Rustfmt { build_compiler: self.compiler, target: self.target @@ -273,13 +273,13 @@ install!((self, builder, _config), ); } }; - Rustc, path = "compiler/rustc", true, only_hosts: true, { + Rustc, path = "compiler/rustc", true, IS_HOST: true, { let tarball = builder.ensure(dist::Rustc { compiler: builder.compiler(builder.top_stage, self.target), }); install_sh(builder, "rustc", self.compiler.stage, Some(self.target), &tarball); }; - RustcCodegenCranelift, alias = "rustc-codegen-cranelift", Self::should_build(_config), only_hosts: true, { + RustcCodegenCranelift, alias = "rustc-codegen-cranelift", Self::should_build(_config), IS_HOST: true, { if let Some(tarball) = builder.ensure(dist::CraneliftCodegenBackend { build_compiler: self.compiler, target: self.target @@ -292,7 +292,7 @@ install!((self, builder, _config), ); } }; - LlvmBitcodeLinker, alias = "llvm-bitcode-linker", Self::should_build(_config), only_hosts: true, { + LlvmBitcodeLinker, alias = "llvm-bitcode-linker", Self::should_build(_config), IS_HOST: true, { if let Some(tarball) = builder.ensure(dist::LlvmBitcodeLinker { build_compiler: self.compiler, target: self.target }) { install_sh(builder, "llvm-bitcode-linker", self.compiler.stage, Some(self.target), &tarball); } else { @@ -311,7 +311,7 @@ pub struct Src { impl Step for Src { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let config = &run.builder.config; diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 16941a32bb16e..260108292e031 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -15,8 +15,6 @@ use std::sync::OnceLock; use std::{env, fs}; use build_helper::git::PathFreshness; -#[cfg(feature = "tracing")] -use tracing::instrument; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step, StepMetadata}; use crate::core::config::{Config, TargetSelection}; @@ -255,7 +253,7 @@ pub struct Llvm { impl Step for Llvm { type Output = LlvmResult; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/llvm-project").path("src/llvm-project/llvm") @@ -266,15 +264,6 @@ impl Step for Llvm { } /// Compile LLVM for `target`. - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "Llvm::run", - skip_all, - fields(target = ?self.target), - ), - )] fn run(self, builder: &Builder<'_>) -> LlvmResult { let target = self.target; let target_native = if self.target.starts_with("riscv") { @@ -908,7 +897,7 @@ pub struct Enzyme { impl Step for Enzyme { type Output = PathBuf; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/enzyme/enzyme") @@ -919,15 +908,6 @@ impl Step for Enzyme { } /// Compile Enzyme for `target`. - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "Enzyme::run", - skip_all, - fields(target = ?self.target), - ), - )] fn run(self, builder: &Builder<'_>) -> PathBuf { builder.require_submodule( "src/tools/enzyme", @@ -1013,7 +993,7 @@ pub struct Lld { impl Step for Lld { type Output = PathBuf; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/llvm-project/lld") diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 962dd372849d2..7f1a7d0025742 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -22,7 +22,7 @@ pub struct BuildManifest; impl Step for BuildManifest { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/build-manifest") @@ -61,7 +61,7 @@ pub struct BumpStage0; impl Step for BumpStage0 { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/bump-stage0") @@ -83,7 +83,7 @@ pub struct ReplaceVersionPlaceholder; impl Step for ReplaceVersionPlaceholder { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/replace-version-placeholder") @@ -107,7 +107,6 @@ pub struct Miri { impl Step for Miri { type Output = (); - const ONLY_HOSTS: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/miri") @@ -175,7 +174,7 @@ pub struct CollectLicenseMetadata; impl Step for CollectLicenseMetadata { type Output = PathBuf; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/collect-license-metadata") @@ -206,7 +205,7 @@ pub struct GenerateCopyright; impl Step for GenerateCopyright { type Output = Vec; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/generate-copyright") @@ -270,7 +269,7 @@ pub struct GenerateWindowsSys; impl Step for GenerateWindowsSys { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/generate-windows-sys") @@ -332,7 +331,7 @@ pub struct UnicodeTableGenerator; impl Step for UnicodeTableGenerator { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/unicode-table-generator") @@ -354,7 +353,7 @@ pub struct FeaturesStatusDump; impl Step for FeaturesStatusDump { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/features-status-dump") @@ -416,7 +415,7 @@ impl Step for CoverageDump { type Output = (); const DEFAULT: bool = false; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/coverage-dump") @@ -438,7 +437,7 @@ pub struct Rustfmt; impl Step for Rustfmt { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/rustfmt") diff --git a/src/bootstrap/src/core/build_steps/synthetic_targets.rs b/src/bootstrap/src/core/build_steps/synthetic_targets.rs index 477ff9553a480..21733c5d9e3f7 100644 --- a/src/bootstrap/src/core/build_steps/synthetic_targets.rs +++ b/src/bootstrap/src/core/build_steps/synthetic_targets.rs @@ -21,7 +21,6 @@ pub(crate) struct MirOptPanicAbortSyntheticTarget { impl Step for MirOptPanicAbortSyntheticTarget { type Output = TargetSelection; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.never() diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index f1be0af31837b..4006bed4ac524 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -10,8 +10,6 @@ use std::path::{Path, PathBuf}; use std::{env, fs, iter}; use build_helper::exit; -#[cfg(feature = "tracing")] -use tracing::instrument; use crate::core::build_steps::compile::{Std, run_cargo}; use crate::core::build_steps::doc::DocumentationFormat; @@ -51,7 +49,7 @@ pub struct CrateBootstrap { impl Step for CrateBootstrap { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -109,7 +107,7 @@ pub struct Linkcheck { impl Step for Linkcheck { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; const DEFAULT: bool = true; /// Runs the `linkchecker` tool as compiled in `stage` by the `host` compiler. @@ -189,7 +187,7 @@ pub struct HtmlCheck { impl Step for HtmlCheck { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; @@ -236,7 +234,7 @@ pub struct Cargotest { impl Step for Cargotest { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/cargotest") @@ -313,7 +311,7 @@ impl Cargo { impl Step for Cargo { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path(Self::CRATE_PATH) @@ -410,7 +408,7 @@ pub struct RustAnalyzer { impl Step for RustAnalyzer { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -469,7 +467,7 @@ pub struct Rustfmt { impl Step for Rustfmt { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/rustfmt") @@ -561,7 +559,6 @@ impl Miri { impl Step for Miri { type Output = (); - const ONLY_HOSTS: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/miri") @@ -681,7 +678,6 @@ pub struct CargoMiri { impl Step for CargoMiri { type Output = (); - const ONLY_HOSTS: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/miri/cargo-miri") @@ -760,10 +756,6 @@ impl Step for CompiletestTest { } /// Runs `cargo test` for compiletest. - #[cfg_attr( - feature = "tracing", - instrument(level = "debug", name = "CompiletestTest::run", skip_all) - )] fn run(self, builder: &Builder<'_>) { let host = self.host; @@ -811,7 +803,7 @@ pub struct Clippy { impl Step for Clippy { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; const DEFAULT: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -911,7 +903,7 @@ pub struct RustdocTheme { impl Step for RustdocTheme { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/rustdoc-themes") @@ -948,7 +940,7 @@ pub struct RustdocJSStd { impl Step for RustdocJSStd { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let default = run.builder.config.nodejs.is_some(); @@ -1008,7 +1000,7 @@ pub struct RustdocJSNotStd { impl Step for RustdocJSNotStd { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let default = run.builder.config.nodejs.is_some(); @@ -1063,7 +1055,7 @@ pub struct RustdocGUI { impl Step for RustdocGUI { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; @@ -1156,7 +1148,7 @@ pub struct Tidy; impl Step for Tidy { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; /// Runs the `tidy` tool. /// @@ -1274,7 +1266,7 @@ macro_rules! test { mode: $mode:expr, suite: $suite:expr, default: $default:expr - $( , only_hosts: $only_hosts:expr )? // default: false + $( , IS_HOST: $IS_HOST:expr )? // default: false $( , compare_mode: $compare_mode:expr )? // default: None $( , )? // optional trailing comma } @@ -1289,10 +1281,10 @@ macro_rules! test { impl Step for $name { type Output = (); const DEFAULT: bool = $default; - const ONLY_HOSTS: bool = (const { + const IS_HOST: bool = (const { #[allow(unused_assignments, unused_mut)] let mut value = false; - $( value = $only_hosts; )? + $( value = $IS_HOST; )? value }); @@ -1340,7 +1332,7 @@ pub struct CrateRunMakeSupport { impl Step for CrateRunMakeSupport { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/run-make-support") @@ -1377,7 +1369,7 @@ pub struct CrateBuildHelper { impl Step for CrateBuildHelper { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/build_helper") @@ -1445,7 +1437,7 @@ test!(UiFullDeps { mode: "ui", suite: "ui-fulldeps", default: true, - only_hosts: true, + IS_HOST: true, }); test!(Rustdoc { @@ -1453,14 +1445,14 @@ test!(Rustdoc { mode: "rustdoc", suite: "rustdoc", default: true, - only_hosts: true, + IS_HOST: true, }); test!(RustdocUi { path: "tests/rustdoc-ui", mode: "ui", suite: "rustdoc-ui", default: true, - only_hosts: true, + IS_HOST: true, }); test!(RustdocJson { @@ -1468,7 +1460,7 @@ test!(RustdocJson { mode: "rustdoc-json", suite: "rustdoc-json", default: true, - only_hosts: true, + IS_HOST: true, }); test!(Pretty { @@ -1476,7 +1468,7 @@ test!(Pretty { mode: "pretty", suite: "pretty", default: true, - only_hosts: true, + IS_HOST: true, }); test!(RunMake { path: "tests/run-make", mode: "run-make", suite: "run-make", default: true }); @@ -1507,7 +1499,7 @@ impl Step for Coverage { type Output = (); const DEFAULT: bool = true; /// Compiletest will automatically skip the "coverage-run" tests if necessary. - const ONLY_HOSTS: bool = false; + const IS_HOST: bool = false; fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> { // Support various invocation styles, including: @@ -1586,7 +1578,7 @@ test!(CoverageRunRustdoc { mode: "coverage-run", suite: "coverage-run-rustdoc", default: true, - only_hosts: true, + IS_HOST: true, }); // For the mir-opt suite we do not use macros, as we need custom behavior when blessing. @@ -1599,7 +1591,6 @@ pub struct MirOpt { impl Step for MirOpt { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.suite_path("tests/mir-opt") @@ -2276,7 +2267,7 @@ struct BookTest { impl Step for BookTest { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.never() @@ -2442,7 +2433,7 @@ macro_rules! test_book { impl Step for $name { type Output = (); const DEFAULT: bool = $default; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path($path) @@ -2502,7 +2493,7 @@ pub struct ErrorIndex { impl Step for ErrorIndex { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { // Also add `error-index` here since that is what appears in the error message @@ -2598,7 +2589,7 @@ pub struct CrateLibrustc { impl Step for CrateLibrustc { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.crate_or_deps("rustc-main").path("compiler") @@ -2883,7 +2874,7 @@ pub struct CrateRustdoc { impl Step for CrateRustdoc { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.paths(&["src/librustdoc", "src/tools/rustdoc"]) @@ -2975,7 +2966,7 @@ pub struct CrateRustdocJsonTypes { impl Step for CrateRustdocJsonTypes { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/rustdoc-json-types") @@ -3164,7 +3155,7 @@ pub struct Bootstrap; impl Step for Bootstrap { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; /// Tests the build system itself. fn run(self, builder: &Builder<'_>) { @@ -3234,7 +3225,7 @@ pub struct TierCheck { impl Step for TierCheck { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/tier-check") @@ -3288,7 +3279,7 @@ pub struct LintDocs { impl Step for LintDocs { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/lint-docs") @@ -3314,7 +3305,7 @@ pub struct RustInstaller; impl Step for RustInstaller { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; const DEFAULT: bool = true; /// Ensure the version placeholder replacement tool builds @@ -3434,7 +3425,7 @@ pub struct CodegenCranelift { impl Step for CodegenCranelift { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.paths(&["compiler/rustc_codegen_cranelift"]) @@ -3562,7 +3553,7 @@ pub struct CodegenGCC { impl Step for CodegenGCC { type Output = (); const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.paths(&["compiler/rustc_codegen_gcc"]) @@ -3695,7 +3686,7 @@ pub struct TestFloatParse { impl Step for TestFloatParse { type Output = (); - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -3761,7 +3752,7 @@ pub struct CollectLicenseMetadata; impl Step for CollectLicenseMetadata { type Output = PathBuf; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/collect-license-metadata") diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 793e6b629c991..b62c9a906b79c 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -13,9 +13,6 @@ use std::ffi::OsStr; use std::path::PathBuf; use std::{env, fs}; -#[cfg(feature = "tracing")] -use tracing::instrument; - use crate::core::build_steps::compile::is_lto_stage; use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, llvm}; @@ -229,6 +226,14 @@ pub fn prepare_tool_cargo( // own copy cargo.env("LZMA_API_STATIC", "1"); + // Build jemalloc on AArch64 with support for page sizes up to 64K + // See: https://github.com/rust-lang/rust/pull/135081 + // Note that `miri` always uses jemalloc. As such, there is no checking of the jemalloc build flag. + // See also the "JEMALLOC_SYS_WITH_LG_PAGE" setting in the compile build step. + if target.starts_with("aarch64") && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() { + cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16"); + } + // CFG_RELEASE is needed by rustfmt (and possibly other tools) which // import rustc-ap-rustc_attr which requires this to be set for the // `#[cfg(version(...))]` attribute. @@ -415,14 +420,6 @@ macro_rules! bootstrap_tool { }); } - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = $tool_name, - skip_all, - ), - )] fn run(self, builder: &Builder<'_>) -> ToolBuildResult { $( for submodule in $submodules { @@ -687,7 +684,7 @@ impl Step for Rustdoc { type Output = PathBuf; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/rustdoc").path("src/librustdoc") @@ -809,7 +806,7 @@ impl Cargo { impl Step for Cargo { type Output = ToolBuildResult; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; @@ -885,7 +882,7 @@ impl LldWrapper { impl Step for LldWrapper { type Output = BuiltLldWrapper; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/lld-wrapper") @@ -901,15 +898,6 @@ impl Step for LldWrapper { }); } - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "LldWrapper::run", - skip_all, - fields(build_compiler = ?self.build_compiler), - ), - )] fn run(self, builder: &Builder<'_>) -> Self::Output { let lld_dir = builder.ensure(llvm::Lld { target: self.target }); let tool = builder.ensure(ToolBuild { @@ -986,7 +974,7 @@ impl WasmComponentLd { impl Step for WasmComponentLd { type Output = ToolBuildResult; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/wasm-component-ld") @@ -1002,15 +990,6 @@ impl Step for WasmComponentLd { }); } - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "WasmComponentLd::run", - skip_all, - fields(build_compiler = ?self.build_compiler), - ), - )] fn run(self, builder: &Builder<'_>) -> ToolBuildResult { builder.ensure(ToolBuild { build_compiler: self.build_compiler, @@ -1049,7 +1028,7 @@ impl RustAnalyzer { impl Step for RustAnalyzer { type Output = ToolBuildResult; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; @@ -1102,7 +1081,7 @@ impl Step for RustAnalyzerProcMacroSrv { type Output = ToolBuildResult; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; @@ -1192,7 +1171,7 @@ impl LlvmBitcodeLinker { impl Step for LlvmBitcodeLinker { type Output = ToolBuildResult; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; @@ -1207,10 +1186,6 @@ impl Step for LlvmBitcodeLinker { }); } - #[cfg_attr( - feature = "tracing", - instrument(level = "debug", name = "LlvmBitcodeLinker::run", skip_all) - )] fn run(self, builder: &Builder<'_>) -> ToolBuildResult { builder.ensure(ToolBuild { build_compiler: self.build_compiler, @@ -1246,7 +1221,7 @@ pub enum LibcxxVersion { impl Step for LibcxxVersionTool { type Output = LibcxxVersion; const DEFAULT: bool = false; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.never() @@ -1407,7 +1382,7 @@ macro_rules! tool_rustc_extended { impl Step for $name { type Output = ToolBuildResult; const DEFAULT: bool = true; // Overridden by `should_run_tool_build_step` - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { should_run_extended_rustc_tool( @@ -1575,7 +1550,7 @@ impl TestFloatParse { impl Step for TestFloatParse { type Output = ToolBuildResult; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; const DEFAULT: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { diff --git a/src/bootstrap/src/core/build_steps/vendor.rs b/src/bootstrap/src/core/build_steps/vendor.rs index 0caeb328811aa..7b860ceb94325 100644 --- a/src/bootstrap/src/core/build_steps/vendor.rs +++ b/src/bootstrap/src/core/build_steps/vendor.rs @@ -53,7 +53,7 @@ pub(crate) struct Vendor { impl Step for Vendor { type Output = VendorOutput; const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; + const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.alias("placeholder").default_condition(true) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 757b9277ec65d..3ce21eb151c3c 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -499,6 +499,10 @@ impl Builder<'_> { build_stamp::clear_if_dirty(self, &out_dir, &backend); } + if self.config.cmd.timings() { + cargo.arg("--timings"); + } + if cmd_kind == Kind::Doc { let my_out = match mode { // This is the intended out directory for compiler documentation. diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 54bf1842ab31b..6226c81a3fdb7 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -100,8 +100,13 @@ pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash { /// by `Step::should_run`. const DEFAULT: bool = false; - /// If true, then this rule should be skipped if --target was specified, but --host was not - const ONLY_HOSTS: bool = false; + /// If this value is true, then the values of `run.target` passed to the `make_run` function of + /// this Step will be determined based on the `--host` flag. + /// If this value is false, then they will be determined based on the `--target` flag. + /// + /// A corollary of the above is that if this is set to true, then the step will be skipped if + /// `--target` was specified, but `--host` was explicitly set to '' (empty string). + const IS_HOST: bool = false; /// Primary function to implement `Step` logic. /// @@ -298,7 +303,7 @@ pub fn crate_description(crates: &[impl AsRef]) -> String { struct StepDescription { default: bool, - only_hosts: bool, + is_host: bool, should_run: fn(ShouldRun<'_>) -> ShouldRun<'_>, make_run: fn(RunConfig<'_>), name: &'static str, @@ -500,7 +505,7 @@ impl StepDescription { fn from(kind: Kind) -> StepDescription { StepDescription { default: S::DEFAULT, - only_hosts: S::ONLY_HOSTS, + is_host: S::IS_HOST, should_run: S::should_run, make_run: S::make_run, name: std::any::type_name::(), @@ -516,7 +521,7 @@ impl StepDescription { } // Determine the targets participating in this rule. - let targets = if self.only_hosts { &builder.hosts } else { &builder.targets }; + let targets = if self.is_host { &builder.hosts } else { &builder.targets }; for target in targets { let run = RunConfig { builder, paths: pathsets.clone(), target: *target }; @@ -951,6 +956,9 @@ impl Step for Libdir { } } +#[cfg(feature = "tracing")] +pub const STEP_SPAN_TARGET: &str = "STEP"; + impl<'a> Builder<'a> { fn get_step_descriptions(kind: Kind) -> Vec { macro_rules! describe { @@ -1271,7 +1279,7 @@ impl<'a> Builder<'a> { pub fn new(build: &Build) -> Builder<'_> { let paths = &build.config.paths; let (kind, paths) = match build.config.cmd { - Subcommand::Build => (Kind::Build, &paths[..]), + Subcommand::Build { .. } => (Kind::Build, &paths[..]), Subcommand::Check { .. } => (Kind::Check, &paths[..]), Subcommand::Clippy { .. } => (Kind::Clippy, &paths[..]), Subcommand::Fix => (Kind::Fix, &paths[..]), @@ -1680,8 +1688,6 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s panic!("{}", out); } if let Some(out) = self.cache.get(&step) { - self.verbose_than(1, || println!("{}c {:?}", " ".repeat(stack.len()), step)); - #[cfg(feature = "tracing")] { if let Some(parent) = stack.last() { @@ -1691,7 +1697,6 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s } return out; } - self.verbose_than(1, || println!("{}> {:?}", " ".repeat(stack.len()), step)); #[cfg(feature = "tracing")] { @@ -1706,10 +1711,29 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s #[cfg(feature = "build-metrics")] self.metrics.enter_step(&step, self); + if self.config.print_step_timings && !self.config.dry_run() { + println!("[TIMING:start] {}", pretty_print_step(&step)); + } + let (out, dur) = { let start = Instant::now(); let zero = Duration::new(0, 0); let parent = self.time_spent_on_dependencies.replace(zero); + + #[cfg(feature = "tracing")] + let _span = { + // Keep the target and field names synchronized with `setup_tracing`. + let span = tracing::info_span!( + target: STEP_SPAN_TARGET, + // We cannot use a dynamic name here, so instead we record the actual step name + // in the step_name field. + "step", + step_name = pretty_step_name::(), + args = step_debug_args(&step) + ); + span.entered() + }; + let out = step.clone().run(self); let dur = start.elapsed(); let deps = self.time_spent_on_dependencies.replace(parent + dur); @@ -1717,13 +1741,9 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s }; if self.config.print_step_timings && !self.config.dry_run() { - let step_string = format!("{step:?}"); - let brace_index = step_string.find('{').unwrap_or(0); - let type_string = type_name::(); println!( - "[TIMING] {} {} -- {}.{:03}", - &type_string.strip_prefix("bootstrap::").unwrap_or(type_string), - &step_string[brace_index..], + "[TIMING:end] {} -- {}.{:03}", + pretty_print_step(&step), dur.as_secs(), dur.subsec_millis() ); @@ -1737,7 +1757,6 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s let cur_step = stack.pop().expect("step stack empty"); assert_eq!(cur_step.downcast_ref(), Some(&step)); } - self.verbose_than(1, || println!("{}< {:?}", " ".repeat(self.stack.borrow().len()), step)); self.cache.put(step, out.clone()); out } @@ -1810,6 +1829,25 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s } } +/// Return qualified step name, e.g. `compile::Rustc`. +pub fn pretty_step_name() -> String { + // Normalize step type path to only keep the module and the type name + let path = type_name::().rsplit("::").take(2).collect::>(); + path.into_iter().rev().collect::>().join("::") +} + +/// Renders `step` using its `Debug` implementation and extract the field arguments out of it. +fn step_debug_args(step: &S) -> String { + let step_dbg_repr = format!("{step:?}"); + let brace_start = step_dbg_repr.find('{').unwrap_or(0); + let brace_end = step_dbg_repr.rfind('}').unwrap_or(step_dbg_repr.len()); + step_dbg_repr[brace_start + 1..brace_end - 1].trim().to_string() +} + +fn pretty_print_step(step: &S) -> String { + format!("{} {{ {} }}", pretty_step_name::(), step_debug_args(step)) +} + impl<'a> AsRef for Builder<'a> { fn as_ref(&self) -> &ExecutionContext { self.exec_ctx() diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 9ced81e1e283d..5eea54360238d 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1342,7 +1342,7 @@ impl Config { Subcommand::Doc { .. } => { flags_stage.or(build_doc_stage).unwrap_or(if download_rustc { 2 } else { 1 }) } - Subcommand::Build => { + Subcommand::Build { .. } => { flags_stage.or(build_build_stage).unwrap_or(if download_rustc { 2 } else { 1 }) } Subcommand::Test { .. } | Subcommand::Miri { .. } => { @@ -1363,7 +1363,7 @@ impl Config { // Now check that the selected stage makes sense, and if not, print a warning and end match (config.stage, &config.cmd) { - (0, Subcommand::Build) => { + (0, Subcommand::Build { .. }) => { eprintln!("ERROR: cannot build anything on stage 0. Use at least stage 1."); exit!(1); } @@ -1396,7 +1396,7 @@ impl Config { Subcommand::Test { .. } | Subcommand::Miri { .. } | Subcommand::Doc { .. } - | Subcommand::Build + | Subcommand::Build { .. } | Subcommand::Bench { .. } | Subcommand::Dist | Subcommand::Install => { diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 31a427f9ffaac..17bfb388280a9 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -241,7 +241,7 @@ fn normalize_args(args: &[String]) -> Vec { it.collect() } -#[derive(Debug, Clone, Default, clap::Subcommand)] +#[derive(Debug, Clone, clap::Subcommand)] pub enum Subcommand { #[command(aliases = ["b"], long_about = "\n Arguments: @@ -256,8 +256,11 @@ pub enum Subcommand { ./x.py build --stage 0 ./x.py build ")] /// Compile either the compiler or libraries - #[default] - Build, + Build { + #[arg(long)] + /// Pass `--timings` to Cargo to get crate build timings + timings: bool, + }, #[command(aliases = ["c"], long_about = "\n Arguments: This subcommand accepts a number of paths to directories to the crates @@ -269,6 +272,9 @@ pub enum Subcommand { #[arg(long)] /// Check all targets all_targets: bool, + #[arg(long)] + /// Pass `--timings` to Cargo to get crate build timings + timings: bool, }, /// Run Clippy (uses rustup/cargo-installed clippy binary) #[command(long_about = "\n @@ -494,11 +500,17 @@ Arguments: Perf(PerfArgs), } +impl Default for Subcommand { + fn default() -> Self { + Subcommand::Build { timings: false } + } +} + impl Subcommand { pub fn kind(&self) -> Kind { match self { Subcommand::Bench { .. } => Kind::Bench, - Subcommand::Build => Kind::Build, + Subcommand::Build { .. } => Kind::Build, Subcommand::Check { .. } => Kind::Check, Subcommand::Clippy { .. } => Kind::Clippy, Subcommand::Doc { .. } => Kind::Doc, @@ -626,6 +638,13 @@ impl Subcommand { } } + pub fn timings(&self) -> bool { + match *self { + Subcommand::Build { timings, .. } | Subcommand::Check { timings, .. } => timings, + _ => false, + } + } + pub fn vendor_versioned_dirs(&self) -> bool { match *self { Subcommand::Vendor { versioned_dirs, .. } => versioned_dirs, diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 0b65ebc067189..706a3cbb2109b 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -37,14 +37,14 @@ use crate::core::builder; use crate::core::builder::Kind; use crate::core::config::{DryRun, LldMode, LlvmLibunwind, TargetSelection, flags}; use crate::utils::exec::{BootstrapCommand, command}; -use crate::utils::helpers::{ - self, dir_is_empty, exe, libdir, set_file_times, split_debuginfo, symlink_dir, -}; +use crate::utils::helpers::{self, dir_is_empty, exe, libdir, set_file_times, split_debuginfo}; mod core; mod utils; pub use core::builder::PathSet; +#[cfg(feature = "tracing")] +pub use core::builder::STEP_SPAN_TARGET; pub use core::config::flags::{Flags, Subcommand}; pub use core::config::{ChangeId, Config}; @@ -53,7 +53,9 @@ use tracing::{instrument, span}; pub use utils::change_tracker::{ CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes, }; -pub use utils::helpers::PanicTracker; +pub use utils::helpers::{PanicTracker, symlink_dir}; +#[cfg(feature = "tracing")] +pub use utils::tracing::setup_tracing; use crate::core::build_steps::vendor::VENDOR_DIR; @@ -2005,13 +2007,13 @@ to download LLVM rather than building it. &self.config.exec_ctx } - pub fn report_summary(&self, start_time: Instant) { - self.config.exec_ctx.profiler().report_summary(start_time); + pub fn report_summary(&self, path: &Path, start_time: Instant) { + self.config.exec_ctx.profiler().report_summary(path, start_time); } #[cfg(feature = "tracing")] - pub fn report_step_graph(self) { - self.step_graph.into_inner().store_to_dot_files(); + pub fn report_step_graph(self, directory: &Path) { + self.step_graph.into_inner().store_to_dot_files(directory); } } diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index 03760faec690b..9a536f75ab741 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -15,7 +15,6 @@ use std::hash::Hash; use std::io::{BufWriter, Write}; use std::panic::Location; use std::path::Path; -use std::process; use std::process::{ Child, ChildStderr, ChildStdout, Command, CommandArgs, CommandEnvs, ExitStatus, Output, Stdio, }; @@ -26,10 +25,8 @@ use build_helper::ci::CiEnv; use build_helper::drop_bomb::DropBomb; use build_helper::exit; -use crate::PathBuf; use crate::core::config::DryRun; -#[cfg(feature = "tracing")] -use crate::trace_cmd; +use crate::{PathBuf, t}; /// What should be done when the command fails. #[derive(Debug, Copy, Clone)] @@ -77,9 +74,17 @@ pub struct CommandFingerprint { } impl CommandFingerprint { + #[cfg(feature = "tracing")] + pub(crate) fn program_name(&self) -> String { + Path::new(&self.program) + .file_name() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_else(|| "".to_string()) + } + /// Helper method to format both Command and BootstrapCommand as a short execution line, /// without all the other details (e.g. environment variables). - pub fn format_short_cmd(&self) -> String { + pub(crate) fn format_short_cmd(&self) -> String { use std::fmt::Write; let mut cmd = self.program.to_string_lossy().to_string(); @@ -121,17 +126,9 @@ impl CommandProfiler { entry.traces.push(ExecutionTrace::CacheHit); } - pub fn report_summary(&self, start_time: Instant) { - let pid = process::id(); - let filename = format!("bootstrap-profile-{pid}.txt"); - - let file = match File::create(&filename) { - Ok(f) => f, - Err(e) => { - eprintln!("Failed to create profiler output file: {e}"); - return; - } - }; + /// Report summary of executed commands file at the specified `path`. + pub fn report_summary(&self, path: &Path, start_time: Instant) { + let file = t!(File::create(path)); let mut writer = BufWriter::new(file); let stats = self.stats.lock().unwrap(); @@ -221,8 +218,6 @@ impl CommandProfiler { writeln!(writer, "Total cache hits: {total_cache_hits}").unwrap(); writeln!(writer, "Estimated time saved due to cache hits: {total_saved_duration:.2?}") .unwrap(); - - println!("Command profiler report saved to {filename}"); } } @@ -686,9 +681,6 @@ impl ExecutionContext { ) -> DeferredCommand<'a> { let fingerprint = command.fingerprint(); - #[cfg(feature = "tracing")] - let span_guard = trace_cmd!(command); - if let Some(cached_output) = self.command_cache.get(&fingerprint) { command.mark_as_executed(); self.verbose(|| println!("Cache hit: {command:?}")); @@ -696,6 +688,9 @@ impl ExecutionContext { return DeferredCommand { state: CommandState::Cached(cached_output) }; } + #[cfg(feature = "tracing")] + let span_guard = crate::utils::tracing::trace_cmd(command); + let created_at = command.get_created_location(); let executed_at = std::panic::Location::caller(); @@ -779,7 +774,7 @@ impl ExecutionContext { } #[cfg(feature = "tracing")] - let span_guard = trace_cmd!(command); + let span_guard = crate::utils::tracing::trace_cmd(command); let start_time = Instant::now(); let fingerprint = command.fingerprint(); diff --git a/src/bootstrap/src/utils/step_graph.rs b/src/bootstrap/src/utils/step_graph.rs index c45825a42223a..cffe0dbb3e3a5 100644 --- a/src/bootstrap/src/utils/step_graph.rs +++ b/src/bootstrap/src/utils/step_graph.rs @@ -1,8 +1,10 @@ use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::io::BufWriter; +use std::path::Path; -use crate::core::builder::{AnyDebug, Step}; +use crate::core::builder::{AnyDebug, Step, pretty_step_name}; +use crate::t; /// Records the executed steps and their dependencies in a directed graph, /// which can then be rendered into a DOT file for visualization. @@ -41,13 +43,7 @@ impl StepGraph { metadata.get_target() ) } else { - let type_name = std::any::type_name::(); - type_name - .strip_prefix("bootstrap::core::") - .unwrap_or(type_name) - .strip_prefix("build_steps::") - .unwrap_or(type_name) - .to_string() + pretty_step_name::() }; let node = Node { label, tooltip: node_key.clone() }; @@ -80,10 +76,10 @@ impl StepGraph { } } - pub fn store_to_dot_files(self) { + pub fn store_to_dot_files(self, directory: &Path) { for (key, graph) in self.graphs.into_iter() { - let filename = format!("bootstrap-steps{key}.dot"); - graph.render(&filename).unwrap(); + let filename = directory.join(format!("step-graph{key}.dot")); + t!(graph.render(&filename)); } } } @@ -147,7 +143,7 @@ impl DotGraph { self.key_to_index.get(key).copied() } - fn render(&self, path: &str) -> std::io::Result<()> { + fn render(&self, path: &Path) -> std::io::Result<()> { use std::io::Write; let mut file = BufWriter::new(std::fs::File::create(path)?); diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs index 109407bc5f235..428ba013c985f 100644 --- a/src/bootstrap/src/utils/tracing.rs +++ b/src/bootstrap/src/utils/tracing.rs @@ -48,17 +48,369 @@ macro_rules! error { } } -#[macro_export] -macro_rules! trace_cmd { - ($cmd:expr) => { +#[cfg(feature = "tracing")] +const COMMAND_SPAN_TARGET: &str = "COMMAND"; + +#[cfg(feature = "tracing")] +pub fn trace_cmd(command: &crate::BootstrapCommand) -> tracing::span::EnteredSpan { + let fingerprint = command.fingerprint(); + let location = command.get_created_location(); + let location = format!("{}:{}", location.file(), location.line()); + + tracing::span!( + target: COMMAND_SPAN_TARGET, + tracing::Level::TRACE, + "cmd", + cmd_name = fingerprint.program_name().to_string(), + cmd = fingerprint.format_short_cmd(), + full_cmd = ?command, + location + ) + .entered() +} + +// # Note on `tracing` usage in bootstrap +// +// Due to the conditional compilation via the `tracing` cargo feature, this means that `tracing` +// usages in bootstrap need to be also gated behind the `tracing` feature: +// +// - `tracing` macros with log levels (`trace!`, `debug!`, `warn!`, `info`, `error`) should not be +// used *directly*. You should use the wrapped `tracing` macros which gate the actual invocations +// behind `feature = "tracing"`. +// - `tracing`'s `#[instrument(..)]` macro will need to be gated like `#![cfg_attr(feature = +// "tracing", instrument(..))]`. +#[cfg(feature = "tracing")] +mod inner { + use std::fmt::Debug; + use std::fs::File; + use std::io::Write; + use std::sync::atomic::Ordering; + + use chrono::{DateTime, Utc}; + use tracing::field::{Field, Visit}; + use tracing::{Event, Id, Level, Subscriber}; + use tracing_subscriber::layer::{Context, SubscriberExt}; + use tracing_subscriber::registry::{LookupSpan, SpanRef}; + use tracing_subscriber::{EnvFilter, Layer}; + + use crate::STEP_SPAN_TARGET; + use crate::utils::tracing::COMMAND_SPAN_TARGET; + + pub fn setup_tracing(env_name: &str) -> TracingGuard { + let filter = EnvFilter::from_env(env_name); + + let registry = tracing_subscriber::registry().with(filter).with(TracingPrinter::default()); + + // When we're creating this layer, we do not yet know the location of the tracing output + // directory, because it is stored in the output directory determined after Config is parsed, + // but we already want to make tracing calls during (and before) config parsing. + // So we store the output into a temporary file, and then move it to the tracing directory + // before bootstrap ends. + let tempdir = tempfile::TempDir::new().expect("Cannot create temporary directory"); + let chrome_tracing_path = tempdir.path().join("bootstrap-trace.json"); + let file = std::io::BufWriter::new(File::create(&chrome_tracing_path).unwrap()); + + let chrome_layer = tracing_chrome::ChromeLayerBuilder::new() + .writer(file) + .include_args(true) + .name_fn(Box::new(|event_or_span| match event_or_span { + tracing_chrome::EventOrSpan::Event(e) => e.metadata().name().to_string(), + tracing_chrome::EventOrSpan::Span(s) => { + if s.metadata().target() == STEP_SPAN_TARGET + && let Some(extension) = s.extensions().get::() + { + extension.0.clone() + } else if s.metadata().target() == COMMAND_SPAN_TARGET + && let Some(extension) = s.extensions().get::() + { + extension.0.clone() + } else { + s.metadata().name().to_string() + } + } + })); + let (chrome_layer, guard) = chrome_layer.build(); + + tracing::subscriber::set_global_default(registry.with(chrome_layer)).unwrap(); + TracingGuard { guard, _tempdir: tempdir, chrome_tracing_path } + } + + pub struct TracingGuard { + guard: tracing_chrome::FlushGuard, + _tempdir: tempfile::TempDir, + chrome_tracing_path: std::path::PathBuf, + } + + impl TracingGuard { + pub fn copy_to_dir(self, dir: &std::path::Path) { + drop(self.guard); + std::fs::rename(&self.chrome_tracing_path, dir.join("chrome-trace.json")).unwrap(); + } + } + + /// Visitor that extracts both known and unknown field values from events and spans. + #[derive(Default)] + struct FieldValues { + /// Main event message + message: Option, + /// Name of a recorded psna + step_name: Option, + /// Short name of an executed command + cmd_name: Option, + /// The rest of arbitrary event/span fields + fields: Vec<(&'static str, String)>, + } + + impl Visit for FieldValues { + /// Record fields if possible using `record_str`, to avoid rendering simple strings with + /// their `Debug` representation, which adds extra quotes. + fn record_str(&mut self, field: &Field, value: &str) { + match field.name() { + "step_name" => { + self.step_name = Some(value.to_string()); + } + "cmd_name" => { + self.cmd_name = Some(value.to_string()); + } + name => { + self.fields.push((name, value.to_string())); + } + } + } + + fn record_debug(&mut self, field: &Field, value: &dyn Debug) { + let formatted = format!("{value:?}"); + match field.name() { + "message" => { + self.message = Some(formatted); + } + name => { + self.fields.push((name, formatted)); + } + } + } + } + + #[derive(Copy, Clone)] + enum SpanAction { + Enter, + } + + /// Holds the name of a step span, stored in `tracing_subscriber`'s extensions. + struct StepNameExtension(String); + + /// Holds the name of a command span, stored in `tracing_subscriber`'s extensions. + struct CommandNameExtension(String); + + #[derive(Default)] + struct TracingPrinter { + indent: std::sync::atomic::AtomicU32, + span_values: std::sync::Mutex>, + } + + impl TracingPrinter { + fn format_header( + &self, + writer: &mut W, + time: DateTime, + level: &Level, + ) -> std::io::Result<()> { + // Use a fixed-width timestamp without date, that shouldn't be very important + let timestamp = time.format("%H:%M:%S.%3f"); + write!(writer, "{timestamp} ")?; + // Make sure that levels are aligned to the same number of characters, in order not to + // break the layout + write!(writer, "{level:>5} ")?; + write!(writer, "{}", " ".repeat(self.indent.load(Ordering::Relaxed) as usize)) + } + + fn write_event(&self, writer: &mut W, event: &Event<'_>) -> std::io::Result<()> { + let now = Utc::now(); + + self.format_header(writer, now, event.metadata().level())?; + + let mut field_values = FieldValues::default(); + event.record(&mut field_values); + + if let Some(msg) = &field_values.message { + write!(writer, "{msg}")?; + } + + if !field_values.fields.is_empty() { + if field_values.message.is_some() { + write!(writer, " ")?; + } + write!(writer, "[")?; + for (index, (name, value)) in field_values.fields.iter().enumerate() { + write!(writer, "{name} = {value}")?; + if index < field_values.fields.len() - 1 { + write!(writer, ", ")?; + } + } + write!(writer, "]")?; + } + write_location(writer, event.metadata())?; + writeln!(writer)?; + Ok(()) + } + + fn write_span( + &self, + writer: &mut W, + span: SpanRef<'_, S>, + field_values: Option<&FieldValues>, + action: SpanAction, + ) -> std::io::Result<()> + where + S: for<'lookup> LookupSpan<'lookup>, { - ::tracing::span!( - target: "COMMAND", - ::tracing::Level::TRACE, - "executing command", - cmd = $cmd.fingerprint().format_short_cmd(), - full_cmd = ?$cmd - ).entered() + let now = Utc::now(); + + self.format_header(writer, now, span.metadata().level())?; + match action { + SpanAction::Enter => { + write!(writer, "> ")?; + } + } + + fn write_fields<'a, I: IntoIterator, W: Write>( + writer: &mut W, + iter: I, + ) -> std::io::Result<()> { + let items = iter.into_iter().collect::>(); + if !items.is_empty() { + write!(writer, " [")?; + for (index, (name, value)) in items.iter().enumerate() { + write!(writer, "{name} = {value}")?; + if index < items.len() - 1 { + write!(writer, ", ")?; + } + } + write!(writer, "]")?; + } + Ok(()) + } + + // We handle steps specially. We instrument them dynamically in `Builder::ensure`, + // and we want to have custom name for each step span. But tracing doesn't allow setting + // dynamic span names. So we detect step spans here and override their name. + match span.metadata().target() { + // Executed step + STEP_SPAN_TARGET => { + let name = + field_values.and_then(|v| v.step_name.as_deref()).unwrap_or(span.name()); + write!(writer, "{name}")?; + + // There should be only one more field called `args` + if let Some(values) = field_values { + let field = &values.fields[0]; + write!(writer, " {{{}}}", field.1)?; + } + write_location(writer, span.metadata())?; + } + // Executed command + COMMAND_SPAN_TARGET => { + write!(writer, "{}", span.name())?; + if let Some(values) = field_values { + write_fields( + writer, + values.fields.iter().filter(|(name, _)| *name != "location"), + )?; + write!( + writer, + " ({})", + values.fields.iter().find(|(name, _)| *name == "location").unwrap().1 + )?; + } + } + // Other span + _ => { + write!(writer, "{}", span.name())?; + if let Some(values) = field_values { + write_fields(writer, values.fields.iter())?; + } + write_location(writer, span.metadata())?; + } + } + + writeln!(writer)?; + Ok(()) } - }; + } + + fn write_location( + writer: &mut W, + metadata: &'static tracing::Metadata<'static>, + ) -> std::io::Result<()> { + use std::path::{Path, PathBuf}; + + if let Some(filename) = metadata.file() { + // Keep only the module name and file name to make it shorter + let filename: PathBuf = Path::new(filename) + .components() + // Take last two path components + .rev() + .take(2) + .collect::>() + .into_iter() + .rev() + .collect(); + + write!(writer, " ({}", filename.display())?; + if let Some(line) = metadata.line() { + write!(writer, ":{line}")?; + } + write!(writer, ")")?; + } + Ok(()) + } + + impl Layer for TracingPrinter + where + S: Subscriber, + S: for<'lookup> LookupSpan<'lookup>, + { + fn on_new_span(&self, attrs: &tracing::span::Attributes<'_>, id: &Id, ctx: Context<'_, S>) { + // Record value of span fields + // Note that we do not implement changing values of span fields after they are created. + // For that we would also need to implement the `on_record` method + let mut field_values = FieldValues::default(); + attrs.record(&mut field_values); + + // We need to propagate the actual name of the span to the Chrome layer below, because + // it cannot access field values. We do that through extensions. + if attrs.metadata().target() == STEP_SPAN_TARGET + && let Some(step_name) = field_values.step_name.clone() + { + ctx.span(id).unwrap().extensions_mut().insert(StepNameExtension(step_name)); + } else if attrs.metadata().target() == COMMAND_SPAN_TARGET + && let Some(cmd_name) = field_values.cmd_name.clone() + { + ctx.span(id).unwrap().extensions_mut().insert(CommandNameExtension(cmd_name)); + } + self.span_values.lock().unwrap().insert(id.clone(), field_values); + } + + fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) { + let mut writer = std::io::stderr().lock(); + self.write_event(&mut writer, event).unwrap(); + } + + fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { + if let Some(span) = ctx.span(id) { + let mut writer = std::io::stderr().lock(); + let values = self.span_values.lock().unwrap(); + let values = values.get(id); + self.write_span(&mut writer, span, values, SpanAction::Enter).unwrap(); + } + self.indent.fetch_add(1, Ordering::Relaxed); + } + + fn on_exit(&self, _id: &Id, _ctx: Context<'_, S>) { + self.indent.fetch_sub(1, Ordering::Relaxed); + } + } } + +#[cfg(feature = "tracing")] +pub use inner::setup_tracing; diff --git a/src/doc/index.md b/src/doc/index.md index 8ad5b427b552a..892057a8f4db3 100644 --- a/src/doc/index.md +++ b/src/doc/index.md @@ -172,9 +172,9 @@ unsafe Rust. It's also sometimes called "the 'nomicon." [The Unstable Book](unstable-book/index.html) has documentation for unstable features. -### The `rustc` Contribution Guide +### The `rustc` Development Guide -[The `rustc` Guide](https://rustc-dev-guide.rust-lang.org/) +[The `rustc-dev-guide`](https://rustc-dev-guide.rust-lang.org/) documents how the compiler works and how to contribute to it. This is useful if you want to build or modify the Rust compiler from source (e.g. to target something non-standard). diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md index 9c5ebbd36c465..fb90c0fdb4353 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md @@ -1,120 +1,100 @@ # Debugging bootstrap -There are two main ways to debug bootstrap itself. The first is through println logging, and the second is through the `tracing` feature. - -> FIXME: this section should be expanded +There are two main ways of debugging (and profiling bootstrap). The first is through println logging, and the second is through the `tracing` feature. ## `println` logging Bootstrap has extensive unstructured logging. Most of it is gated behind the `--verbose` flag (pass `-vv` for even more detail). -If you want to know which `Step` ran a command, you could invoke bootstrap like so: +If you want to see verbose output of executed Cargo commands and other kinds of detailed logs, pass `-v` or `-vv` when invoking bootstrap. Note that the logs are unstructured and may be overwhelming. ``` $ ./x dist rustc --dry-run -vv learning about cargo running: RUSTC_BOOTSTRAP="1" "/home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "metadata" "--format-version" "1" "--no-deps" "--manifest-path" "/home/jyn/src/rust2/Cargo.toml" (failure_mode=Exit) (created at src/bootstrap/src/core/metadata.rs:81:25, executed at src/bootstrap/src/core/metadata.rs:92:50) running: RUSTC_BOOTSTRAP="1" "/home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "metadata" "--format-version" "1" "--no-deps" "--manifest-path" "/home/jyn/src/rust2/library/Cargo.toml" (failure_mode=Exit) (created at src/bootstrap/src/core/metadata.rs:81:25, executed at src/bootstrap/src/core/metadata.rs:92:50) -> Assemble { target_compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu } } - > Libdir { compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu }, target: x86_64-unknown-linux-gnu } - > Sysroot { compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu }, force_recompile: false } -Removing sysroot /home/jyn/src/rust2/build/tmp-dry-run/x86_64-unknown-linux-gnu/stage1 to avoid caching bugs - < Sysroot { compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu }, force_recompile: false } - < Libdir { compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu }, target: x86_64-unknown-linux-gnu } -... -``` - -This will go through all the recursive dependency calculations, where `Step`s internally call `builder.ensure()`, without actually running cargo or the compiler. - -In some cases, even this may not be enough logging (if so, please add more!). In that case, you can omit `--dry-run`, which will show the normal output inline with the debug logging: - -``` - c Sysroot { compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu }, force_recompile: false } -using sysroot /home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage0-sysroot -Building stage0 library artifacts (x86_64-unknown-linux-gnu) -running: cd "/home/jyn/src/rust2" && env ... RUSTC_VERBOSE="2" RUSTC_WRAPPER="/home/jyn/src/rust2/build/bootstrap/debug/rustc" "/home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "build" "--target" "x86_64-unknown-linux-gnu" "-Zbinary-dep-depinfo" "-Zroot-dir=/home/jyn/src/rust2" "-v" "-v" "--manifest-path" "/home/jyn/src/rust2/library/sysroot/Cargo.toml" "--message-format" "json-render-diagnostics" - 0.293440230s INFO prepare_target{force=false package_id=sysroot v0.0.0 (/home/jyn/src/rust2/library/sysroot) target="sysroot"}: cargo::core::compiler::fingerprint: fingerprint error for sysroot v0.0.0 (/home/jyn/src/rust2/library/sysroot)/Build/TargetInner { name_inferred: true, ..: lib_target("sysroot", ["lib"], "/home/jyn/src/rust2/library/sysroot/src/lib.rs", Edition2021) } ... ``` -In most cases this should not be necessary. +## `tracing` in bootstrap -TODO: we should convert all this to structured logging so it's easier to control precisely. +Bootstrap has a conditional `tracing` feature, which provides the following features: +- It enables structured logging using [`tracing`][tracing] events and spans. +- It generates a [Chrome trace file] that can be used to visualize the hierarchy and durations of executed steps and commands. + - You can open the generated `chrome-trace.json` file using Chrome, on the `chrome://tracing` tab, or e.g. using [Perfetto]. +- It generates [GraphViz] graphs that visualize the dependencies between executed steps. + - You can open the generated `step-graph-*.dot` file using e.g. [xdot] to visualize the step graph, or use e.g. `dot -Tsvg` to convert the GraphViz file to an SVG file. +- It generates a command execution summary, which shows which commands were executed, how many of their executions were cached, and what commands were the slowest to run. + - The generated `command-stats.txt` file is in a simple human-readable format. -## `tracing` in bootstrap +The structured logs will be written to standard error output (`stderr`), while the other outputs will be stored in files in the `/bootstrap-trace/` directory. For convenience, bootstrap will also create a symlink to the latest generated trace output directory at `/bootstrap-trace/latest`. -Bootstrap has conditional [`tracing`][tracing] setup to provide structured logging. +> Note that if you execute bootstrap with `--dry-run`, the tracing output directory might change. Bootstrap will always print a path where the tracing output files were stored at the end of its execution. [tracing]: https://docs.rs/tracing/0.1.41/tracing/index.html +[Chrome trace file]: https://www.chromium.org/developers/how-tos/trace-event-profiling-tool/ +[Perfetto]: https://ui.perfetto.dev/ +[GraphViz]: https://graphviz.org/doc/info/lang.html +[xdot]: https://github.com/jrfonseca/xdot.py ### Enabling `tracing` output -Bootstrap will conditionally build `tracing` support and enable `tracing` output if the `BOOTSTRAP_TRACING` env var is set. - -#### Basic usage - -Example basic usage[^just-trace]: +To enable the conditional `tracing` feature, run bootstrap with the `BOOTSTRAP_TRACING` environment variable. -[^just-trace]: It is not recommended to use *just* `BOOTSTRAP_TRACING=TRACE` because that will dump *everything* at `TRACE` level, including logs intentionally gated behind custom targets as they are too verbose even for `TRACE` level by default. +[tracing_subscriber filter]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html ```bash -$ BOOTSTRAP_TRACING=bootstrap=TRACE ./x build library --stage 1 +$ BOOTSTRAP_TRACING=trace ./x build library --stage 1 ``` Example output[^unstable]: ``` -$ BOOTSTRAP_TRACING=bootstrap=TRACE ./x check src/bootstrap/ +$ BOOTSTRAP_TRACING=trace ./x build library --stage 1 --dry-run Building bootstrap - Compiling bootstrap v0.0.0 (/home/joe/repos/rust/src/bootstrap) - Finished `dev` profile [unoptimized] target(s) in 2.74s - DEBUG bootstrap parsing flags - bootstrap::core::config::flags::Flags::parse args=["check", "src/bootstrap/"] - DEBUG bootstrap parsing config based on flags - DEBUG bootstrap creating new build based on config - bootstrap::Build::build - TRACE bootstrap setting up job management - TRACE bootstrap downloading rustfmt early - bootstrap::handling hardcoded subcommands (Format, Suggest, Perf) - DEBUG bootstrap not a hardcoded subcommand; returning to normal handling, cmd=Check { all_targets: false } - DEBUG bootstrap handling subcommand normally - bootstrap::executing real run - bootstrap::(1) executing dry-run sanity-check - bootstrap::(2) executing actual run -Checking stage0 library artifacts (x86_64-unknown-linux-gnu) - Finished `release` profile [optimized + debuginfo] target(s) in 0.04s -Checking stage0 compiler artifacts {rustc-main, rustc_abi, rustc_arena, rustc_ast, rustc_ast_ir, rustc_ast_lowering, rustc_ast_passes, rustc_ast_pretty, rustc_attr_data_structures, rustc_attr_parsing, rustc_baked_icu_data, rustc_borrowck, rustc_builtin_macros, rustc_codegen_llvm, rustc_codegen_ssa, rustc_const_eval, rustc_data_structures, rustc_driver, rustc_driver_impl, rustc_error_codes, rustc_error_messages, rustc_errors, rustc_expand, rustc_feature, rustc_fluent_macro, rustc_fs_util, rustc_graphviz, rustc_hir, rustc_hir_analysis, rustc_hir_pretty, rustc_hir_typeck, rustc_incremental, rustc_index, rustc_index_macros, rustc_infer, rustc_interface, rustc_lexer, rustc_lint, rustc_lint_defs, rustc_llvm, rustc_log, rustc_macros, rustc_metadata, rustc_middle, rustc_mir_build, rustc_mir_dataflow, rustc_mir_transform, rustc_monomorphize, rustc_next_trait_solver, rustc_parse, rustc_parse_format, rustc_passes, rustc_pattern_analysis, rustc_privacy, rustc_query_impl, rustc_query_system, rustc_resolve, rustc_sanitizers, rustc_serialize, rustc_session, rustc_smir, rustc_span, rustc_symbol_mangling, rustc_target, rustc_trait_selection, rustc_traits, rustc_transmute, rustc_ty_utils, rustc_type_ir, rustc_type_ir_macros, stable_mir} (x86_64-unknown-linux-gnu) - Finished `release` profile [optimized + debuginfo] target(s) in 0.23s -Checking stage0 bootstrap artifacts (x86_64-unknown-linux-gnu) - Checking bootstrap v0.0.0 (/home/joe/repos/rust/src/bootstrap) - Finished `release` profile [optimized + debuginfo] target(s) in 0.64s - DEBUG bootstrap checking for postponed test failures from `test --no-fail-fast` -Build completed successfully in 0:00:08 + Finished `dev` profile [unoptimized] target(s) in 0.05s +15:56:52.477 INFO > tool::LibcxxVersionTool {target: x86_64-unknown-linux-gnu} (builder/mod.rs:1715) +15:56:52.575 INFO > compile::Assemble {target_compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }} (builder/mod.rs:1715) +15:56:52.575 INFO > tool::Compiletest {compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu} (builder/mod.rs:1715) +15:56:52.576 INFO > tool::ToolBuild {build_compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu, tool: "compiletest", path: "src/tools/compiletest", mode: ToolBootstrap, source_type: InTree, extra_features: [], allow_features: "internal_output_capture", cargo_args: [], artifact_kind: Binary} (builder/mod.rs:1715) +15:56:52.576 INFO > builder::Libdir {compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu} (builder/mod.rs:1715) +15:56:52.576 INFO > compile::Sysroot {compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, force_recompile: false} (builder/mod.rs:1715) +15:56:52.578 INFO > compile::Assemble {target_compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }} (builder/mod.rs:1715) +15:56:52.578 INFO > tool::Compiletest {compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu} (builder/mod.rs:1715) +15:56:52.578 INFO > tool::ToolBuild {build_compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu, tool: "compiletest", path: "src/tools/compiletest", mode: ToolBootstrap, source_type: InTree, extra_features: [], allow_features: "internal_output_capture", cargo_args: [], artifact_kind: Binary} (builder/mod.rs:1715) +15:56:52.578 INFO > builder::Libdir {compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu} (builder/mod.rs:1715) +15:56:52.578 INFO > compile::Sysroot {compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, force_recompile: false} (builder/mod.rs:1715) + Finished `release` profile [optimized] target(s) in 0.11s +Tracing/profiling output has been written to /build/bootstrap-trace/latest +Build completed successfully in 0:00:00 ``` +[^unstable]: This output is always subject to further changes. + #### Controlling tracing output -The env var `BOOTSTRAP_TRACING` accepts a [`tracing` env-filter][tracing-env-filter]. +The environment variable `BOOTSTRAP_TRACING` accepts a [`tracing_subscriber` filter][tracing-env-filter]. If you set `BOOTSTRAP_TRACING=trace`, you will enable all logs, but that can be overwhelming. You can thus use the filter to reduce the amount of data logged. There are two orthogonal ways to control which kind of tracing logs you want: -1. You can specify the log **level**, e.g. `DEBUG` or `TRACE`. -2. You can also control the log **target**, e.g. `bootstrap` or `bootstrap::core::config` vs custom targets like `CONFIG_HANDLING`. - - Custom targets are used to limit what is output when `BOOTSTRAP_TRACING=bootstrap=TRACE` is used, as they can be too verbose even for `TRACE` level by default. Currently used custom targets: - - `CONFIG_HANDLING` - -The `TRACE` filter will enable *all* `trace` level or less verbose level tracing output. +1. You can specify the log **level**, e.g. `debug` or `trace`. + - If you select a level, all events/spans with an equal or higher priority level will be shown. +2. You can also control the log **target**, e.g. `bootstrap` or `bootstrap::core::config` or a custom target like `CONFIG_HANDLING` or `STEP`. + - Custom targets are used to limit what kinds of spans you are interested in, as the `BOOTSTRAP_TRACING=trace` output can be quite verbose. Currently, you can use the following custom targets: + - `CONFIG_HANDLING`: show spans related to config handling + - `STEP`: show all executed steps. Note that executed commands have `info` event level. + - `COMMAND`: show all executed commands. Note that executed commands have `trace` event level. You can of course combine them (custom target logs are typically gated behind `TRACE` log level additionally): ```bash -$ BOOTSTRAP_TRACING=CONFIG_HANDLING=TRACE ./x build library --stage 1 +$ BOOTSTRAP_TRACING=CONFIG_HANDLING=trace,STEP=info,COMMAND=trace ./x build library --stage 1 ``` -[^unstable]: This output is always subject to further changes. - [tracing-env-filter]: https://docs.rs/tracing-subscriber/0.3.19/tracing_subscriber/filter/struct.EnvFilter.html +Note that the level that you specify using `BOOTSTRAP_TRACING` also has an effect on the spans that will be recorded in the Chrome trace file. + ##### FIXME(#96176): specific tracing for `compiler()` vs `compiler_for()` The additional targets `COMPILER` and `COMPILER_FOR` are used to help trace what @@ -123,12 +103,6 @@ if [#96176][cleanup-compiler-for] is resolved. [cleanup-compiler-for]: https://github.com/rust-lang/rust/issues/96176 -### Rendering step graph - -When you run bootstrap with the `BOOTSTRAP_TRACING` environment variable configured, bootstrap will automatically output a DOT file that shows all executed steps and their dependencies. The files will have a prefix `bootstrap-steps`. You can use e.g. `xdot` to visualize the file or e.g. `dot -Tsvg` to convert the DOT file to a SVG file. - -A separate DOT file will be outputted for dry-run and non-dry-run execution. - ### Using `tracing` in bootstrap Both `tracing::*` macros and the `tracing::instrument` proc-macro attribute need to be gated behind `tracing` feature. Examples: @@ -149,15 +123,6 @@ impl Step for Foo { todo!() } - #[cfg_attr( - feature = "tracing", - instrument( - level = "trace", - name = "Foo::run", - skip_all, - fields(compiler = ?builder.compiler), - ), - )] fn run(self, builder: &Builder<'_>) -> Self::Output { trace!(?run, "entered Foo::run"); @@ -172,21 +137,6 @@ For `#[instrument]`, it's recommended to: - Explicitly pick an instrumentation name via `name = ".."` to distinguish between e.g. `run` of different steps. - Take care to not cause diverging behavior via tracing, e.g. building extra things only when tracing infra is enabled. -### Profiling bootstrap - -You can set the `BOOTSTRAP_PROFILE=1` environment variable to enable command execution profiling during bootstrap. This generates: - -* A Chrome trace file (for visualization in `chrome://tracing` or [Perfetto](https://ui.perfetto.dev)) if tracing is enabled via `BOOTSTRAP_TRACING=COMMAND=trace` -* A plain-text summary file, `bootstrap-profile-{pid}.txt`, listing all commands sorted by execution time (slowest first), along with cache hits and working directories - -Note: the `.txt` report is always generated when `BOOTSTRAP_PROFILE=1` is set — tracing is not required. - -Example usage: - -```bash -$ BOOTSTRAP_PROFILE=1 BOOTSTRAP_TRACING=COMMAND=trace ./x build library -``` - ### rust-analyzer integration? Unfortunately, because bootstrap is a `rust-analyzer.linkedProjects`, you can't ask r-a to check/build bootstrap itself with `tracing` feature enabled to get relevant completions, due to lack of support as described in . diff --git a/src/etc/completions/x.fish b/src/etc/completions/x.fish index a5e5bb8f09e81..0b9af33421461 100644 --- a/src/etc/completions/x.fish +++ b/src/etc/completions/x.fish @@ -97,6 +97,7 @@ complete -c x -n "__fish_x_using_subcommand build" -l llvm-profile-use -d 'use P complete -c x -n "__fish_x_using_subcommand build" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r complete -c x -n "__fish_x_using_subcommand build" -l set -d 'override options in bootstrap.toml' -r -f complete -c x -n "__fish_x_using_subcommand build" -l ci -d 'Make bootstrap to behave as it\'s running on the CI environment or not' -r -f -a "{true\t'',false\t''}" +complete -c x -n "__fish_x_using_subcommand build" -l timings -d 'Pass `--timings` to Cargo to get crate build timings' complete -c x -n "__fish_x_using_subcommand build" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x -n "__fish_x_using_subcommand build" -s i -l incremental -d 'use incremental compilation' complete -c x -n "__fish_x_using_subcommand build" -l include-default-paths -d 'include default paths in addition to the provided ones' @@ -133,6 +134,7 @@ complete -c x -n "__fish_x_using_subcommand check" -l reproducible-artifact -d ' complete -c x -n "__fish_x_using_subcommand check" -l set -d 'override options in bootstrap.toml' -r -f complete -c x -n "__fish_x_using_subcommand check" -l ci -d 'Make bootstrap to behave as it\'s running on the CI environment or not' -r -f -a "{true\t'',false\t''}" complete -c x -n "__fish_x_using_subcommand check" -l all-targets -d 'Check all targets' +complete -c x -n "__fish_x_using_subcommand check" -l timings -d 'Pass `--timings` to Cargo to get crate build timings' complete -c x -n "__fish_x_using_subcommand check" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x -n "__fish_x_using_subcommand check" -s i -l incremental -d 'use incremental compilation' complete -c x -n "__fish_x_using_subcommand check" -l include-default-paths -d 'include default paths in addition to the provided ones' diff --git a/src/etc/completions/x.ps1 b/src/etc/completions/x.ps1 index 4fee3bc0a8692..95cee4b633625 100644 --- a/src/etc/completions/x.ps1 +++ b/src/etc/completions/x.ps1 @@ -102,6 +102,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in bootstrap.toml') [CompletionResult]::new('--ci', '--ci', [CompletionResultType]::ParameterName, 'Make bootstrap to behave as it''s running on the CI environment or not') + [CompletionResult]::new('--timings', '--timings', [CompletionResultType]::ParameterName, 'Pass `--timings` to Cargo to get crate build timings') [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') @@ -145,6 +146,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in bootstrap.toml') [CompletionResult]::new('--ci', '--ci', [CompletionResultType]::ParameterName, 'Make bootstrap to behave as it''s running on the CI environment or not') [CompletionResult]::new('--all-targets', '--all-targets', [CompletionResultType]::ParameterName, 'Check all targets') + [CompletionResult]::new('--timings', '--timings', [CompletionResultType]::ParameterName, 'Pass `--timings` to Cargo to get crate build timings') [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish index e2e6ae05ee03d..6fba6a4562368 100644 --- a/src/etc/completions/x.py.fish +++ b/src/etc/completions/x.py.fish @@ -97,6 +97,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand build" -l llvm-profile-use -d complete -c x.py -n "__fish_x.py_using_subcommand build" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r complete -c x.py -n "__fish_x.py_using_subcommand build" -l set -d 'override options in bootstrap.toml' -r -f complete -c x.py -n "__fish_x.py_using_subcommand build" -l ci -d 'Make bootstrap to behave as it\'s running on the CI environment or not' -r -f -a "{true\t'',false\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand build" -l timings -d 'Pass `--timings` to Cargo to get crate build timings' complete -c x.py -n "__fish_x.py_using_subcommand build" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_x.py_using_subcommand build" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_x.py_using_subcommand build" -l include-default-paths -d 'include default paths in addition to the provided ones' @@ -133,6 +134,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand check" -l reproducible-artifac complete -c x.py -n "__fish_x.py_using_subcommand check" -l set -d 'override options in bootstrap.toml' -r -f complete -c x.py -n "__fish_x.py_using_subcommand check" -l ci -d 'Make bootstrap to behave as it\'s running on the CI environment or not' -r -f -a "{true\t'',false\t''}" complete -c x.py -n "__fish_x.py_using_subcommand check" -l all-targets -d 'Check all targets' +complete -c x.py -n "__fish_x.py_using_subcommand check" -l timings -d 'Pass `--timings` to Cargo to get crate build timings' complete -c x.py -n "__fish_x.py_using_subcommand check" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_x.py_using_subcommand check" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_x.py_using_subcommand check" -l include-default-paths -d 'include default paths in addition to the provided ones' diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1 index ea3aacc21c7ac..458879a17a7b9 100644 --- a/src/etc/completions/x.py.ps1 +++ b/src/etc/completions/x.py.ps1 @@ -102,6 +102,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in bootstrap.toml') [CompletionResult]::new('--ci', '--ci', [CompletionResultType]::ParameterName, 'Make bootstrap to behave as it''s running on the CI environment or not') + [CompletionResult]::new('--timings', '--timings', [CompletionResultType]::ParameterName, 'Pass `--timings` to Cargo to get crate build timings') [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') @@ -145,6 +146,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in bootstrap.toml') [CompletionResult]::new('--ci', '--ci', [CompletionResultType]::ParameterName, 'Make bootstrap to behave as it''s running on the CI environment or not') [CompletionResult]::new('--all-targets', '--all-targets', [CompletionResultType]::ParameterName, 'Check all targets') + [CompletionResult]::new('--timings', '--timings', [CompletionResultType]::ParameterName, 'Pass `--timings` to Cargo to get crate build timings') [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index f31bdb58dc448..e003bf7fd0b43 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -458,7 +458,7 @@ _x.py() { return 0 ;; x.py__build) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --timings --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -644,7 +644,7 @@ _x.py() { return 0 ;; x.py__check) - opts="-v -i -j -h --all-targets --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --all-targets --timings --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh index 32e986ad141fa..b82c2d65e8694 100644 --- a/src/etc/completions/x.py.zsh +++ b/src/etc/completions/x.py.zsh @@ -90,6 +90,7 @@ _arguments "${_arguments_options[@]}" : \ '*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \ '*--set=[override options in bootstrap.toml]:section.option=value:' \ '--ci=[Make bootstrap to behave as it'\''s running on the CI environment or not]:bool:(true false)' \ +'--timings[Pass \`--timings\` to Cargo to get crate build timings]' \ '*-v[use verbose output (-vv for very verbose)]' \ '*--verbose[use verbose output (-vv for very verbose)]' \ '-i[use incremental compilation]' \ @@ -135,6 +136,7 @@ _arguments "${_arguments_options[@]}" : \ '*--set=[override options in bootstrap.toml]:section.option=value:' \ '--ci=[Make bootstrap to behave as it'\''s running on the CI environment or not]:bool:(true false)' \ '--all-targets[Check all targets]' \ +'--timings[Pass \`--timings\` to Cargo to get crate build timings]' \ '*-v[use verbose output (-vv for very verbose)]' \ '*--verbose[use verbose output (-vv for very verbose)]' \ '-i[use incremental compilation]' \ diff --git a/src/etc/completions/x.sh b/src/etc/completions/x.sh index 927d8f7661cd3..c2cb771002017 100644 --- a/src/etc/completions/x.sh +++ b/src/etc/completions/x.sh @@ -458,7 +458,7 @@ _x() { return 0 ;; x__build) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --timings --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -644,7 +644,7 @@ _x() { return 0 ;; x__check) - opts="-v -i -j -h --all-targets --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --all-targets --timings --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/src/etc/completions/x.zsh b/src/etc/completions/x.zsh index 65995553276d9..49139e70f7fb2 100644 --- a/src/etc/completions/x.zsh +++ b/src/etc/completions/x.zsh @@ -90,6 +90,7 @@ _arguments "${_arguments_options[@]}" : \ '*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \ '*--set=[override options in bootstrap.toml]:section.option=value:' \ '--ci=[Make bootstrap to behave as it'\''s running on the CI environment or not]:bool:(true false)' \ +'--timings[Pass \`--timings\` to Cargo to get crate build timings]' \ '*-v[use verbose output (-vv for very verbose)]' \ '*--verbose[use verbose output (-vv for very verbose)]' \ '-i[use incremental compilation]' \ @@ -135,6 +136,7 @@ _arguments "${_arguments_options[@]}" : \ '*--set=[override options in bootstrap.toml]:section.option=value:' \ '--ci=[Make bootstrap to behave as it'\''s running on the CI environment or not]:bool:(true false)' \ '--all-targets[Check all targets]' \ +'--timings[Pass \`--timings\` to Cargo to get crate build timings]' \ '*-v[use verbose output (-vv for very verbose)]' \ '*--verbose[use verbose output (-vv for very verbose)]' \ '-i[use incremental compilation]' \ diff --git a/tests/ui/borrowck/borrowck-in-static.stderr b/tests/ui/borrowck/borrowck-in-static.stderr index d85f6f5fdd5c7..32419da0ce233 100644 --- a/tests/ui/borrowck/borrowck-in-static.stderr +++ b/tests/ui/borrowck/borrowck-in-static.stderr @@ -10,7 +10,7 @@ LL | Box::new(|| x) | | | captured by this `Fn` closure | - = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once help: consider cloning the value if the performance cost is acceptable | LL | Box::new(|| x.clone()) diff --git a/tests/ui/borrowck/borrowck-move-by-capture.stderr b/tests/ui/borrowck/borrowck-move-by-capture.stderr index e9e054407662f..0ace615628177 100644 --- a/tests/ui/borrowck/borrowck-move-by-capture.stderr +++ b/tests/ui/borrowck/borrowck-move-by-capture.stderr @@ -12,7 +12,7 @@ LL | let _h = to_fn_once(move || -> isize { *bar }); | | | `bar` is moved here | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/borrowck-move-by-capture.rs:3:37 | LL | fn to_fn_mut>(f: F) -> F { f } diff --git a/tests/ui/borrowck/issue-103624.stderr b/tests/ui/borrowck/issue-103624.stderr index ef02280888671..bd6c1c44bfb36 100644 --- a/tests/ui/borrowck/issue-103624.stderr +++ b/tests/ui/borrowck/issue-103624.stderr @@ -13,7 +13,7 @@ LL | LL | self.b; | ^^^^^^ `self.b` is moved here | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/issue-103624.rs:7:36 | LL | async fn spawn_blocking(f: impl (Fn() -> T) + Send + Sync + 'static) -> T { diff --git a/tests/ui/borrowck/issue-87456-point-to-closure.stderr b/tests/ui/borrowck/issue-87456-point-to-closure.stderr index 043e336cd86df..c31d096109ce0 100644 --- a/tests/ui/borrowck/issue-87456-point-to-closure.stderr +++ b/tests/ui/borrowck/issue-87456-point-to-closure.stderr @@ -10,7 +10,7 @@ LL | LL | let _foo: String = val; | ^^^ move occurs because `val` has type `String`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/issue-87456-point-to-closure.rs:3:24 | LL | fn take_mut(_val: impl FnMut()) {} diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index d33330413103f..69c3667491633 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -10,7 +10,7 @@ LL | y.into_iter(); | | | move occurs because `y` has type `Vec`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:5:28 | LL | fn call(f: F) where F : Fn() { diff --git a/tests/ui/consts/const-eval/union-const-eval-field.rs b/tests/ui/consts/const-eval/union-const-eval-field.rs index 719e59b007c00..2c9061a7a50f8 100644 --- a/tests/ui/consts/const-eval/union-const-eval-field.rs +++ b/tests/ui/consts/const-eval/union-const-eval-field.rs @@ -1,5 +1,6 @@ //@ dont-require-annotations: NOTE //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" +//@ normalize-stderr: "([[:xdigit:]]{2}\s){4}(__\s){4}\s+│\s+([?|\.]){4}\W{4}" -> "HEX_DUMP" type Field1 = i32; type Field2 = f32; diff --git a/tests/ui/consts/const-eval/union-const-eval-field.stderr b/tests/ui/consts/const-eval/union-const-eval-field.stderr index 1843ce273ac04..3b7e5508d56c4 100644 --- a/tests/ui/consts/const-eval/union-const-eval-field.stderr +++ b/tests/ui/consts/const-eval/union-const-eval-field.stderr @@ -1,21 +1,21 @@ error[E0080]: reading memory at ALLOC0[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory - --> $DIR/union-const-eval-field.rs:29:37 + --> $DIR/union-const-eval-field.rs:30:37 | LL | const FIELD3: Field3 = unsafe { UNION.field3 }; | ^^^^^^^^^^^^ evaluation of `read_field3::FIELD3` failed here | = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - 00 00 80 3f __ __ __ __ │ ...?░░░░ + HEX_DUMP } note: erroneous constant encountered - --> $DIR/union-const-eval-field.rs:31:5 + --> $DIR/union-const-eval-field.rs:32:5 | LL | FIELD3 | ^^^^^^ note: erroneous constant encountered - --> $DIR/union-const-eval-field.rs:31:5 + --> $DIR/union-const-eval-field.rs:32:5 | LL | FIELD3 | ^^^^^^ diff --git a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs index 15f4a9a778ed6..ed15f5bba9622 100644 --- a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs +++ b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs @@ -1,3 +1,4 @@ +//@ normalize-stderr: "[[:xdigit:]]{2} __ ([[:xdigit:]]{2}\s){2}" -> "HEX_DUMP" #![feature(core_intrinsics)] const RAW_EQ_PADDING: bool = unsafe { diff --git a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr index 5f4ef14d58697..329da35297e1c 100644 --- a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr +++ b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr @@ -1,15 +1,15 @@ error[E0080]: reading memory at ALLOC0[0x0..0x4], but memory is uninitialized at [0x1..0x2], and this operation requires initialized memory - --> $DIR/intrinsic-raw_eq-const-bad.rs:4:5 + --> $DIR/intrinsic-raw_eq-const-bad.rs:5:5 | LL | std::intrinsics::raw_eq(&(1_u8, 2_u16), &(1_u8, 2_u16)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `RAW_EQ_PADDING` failed here | = note: the raw bytes of the constant (size: 4, align: 2) { - 01 __ 02 00 │ .░.. + HEX_DUMP │ .░.. } error[E0080]: unable to turn pointer into integer - --> $DIR/intrinsic-raw_eq-const-bad.rs:9:5 + --> $DIR/intrinsic-raw_eq-const-bad.rs:10:5 | LL | std::intrinsics::raw_eq(&(&0), &(&1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `RAW_EQ_PTR` failed here @@ -18,7 +18,7 @@ LL | std::intrinsics::raw_eq(&(&0), &(&1)) = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: accessing memory with alignment 1, but alignment 4 is required - --> $DIR/intrinsic-raw_eq-const-bad.rs:16:5 + --> $DIR/intrinsic-raw_eq-const-bad.rs:17:5 | LL | std::intrinsics::raw_eq(aref, aref) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `RAW_EQ_NOT_ALIGNED` failed here diff --git a/tests/ui/issues/issue-4335.stderr b/tests/ui/issues/issue-4335.stderr index b6d8f08616383..d1a64e3dd4620 100644 --- a/tests/ui/issues/issue-4335.stderr +++ b/tests/ui/issues/issue-4335.stderr @@ -10,7 +10,7 @@ LL | id(Box::new(|| *v)) | | | captured by this `FnMut` closure | - = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once help: if `T` implemented `Clone`, you could clone the value --> $DIR/issue-4335.rs:5:10 | diff --git a/tests/ui/macros/cfg_select.rs b/tests/ui/macros/cfg_select.rs index 461d2e0e8c1f5..9241141ef9a6c 100644 --- a/tests/ui/macros/cfg_select.rs +++ b/tests/ui/macros/cfg_select.rs @@ -8,10 +8,42 @@ fn print() { }); } -fn arm_rhs_must_be_in_braces() -> i32 { +fn print_2() { + println!(cfg_select! { + unix => "unix", + _ => "not unix", + }); +} + +fn arm_rhs_expr_1() -> i32 { cfg_select! { true => 1 - //~^ ERROR: expected `{`, found `1` + } +} + +fn arm_rhs_expr_2() -> i32 { + cfg_select! { + true => 1, + false => 2 + } +} + +fn arm_rhs_expr_3() -> i32 { + cfg_select! { + true => 1, + false => 2, + true => { 42 } + false => -1 as i32, + true => 2 + 2, + false => "", + true => if true { 42 } else { 84 } + false => if true { 42 } else { 84 }, + true => return 42, + false => loop {} + true => (1, 2), + false => (1, 2,), + true => todo!(), + false => println!("hello"), } } diff --git a/tests/ui/macros/cfg_select.stderr b/tests/ui/macros/cfg_select.stderr index 6c18a7c189dcc..7280f35c16f93 100644 --- a/tests/ui/macros/cfg_select.stderr +++ b/tests/ui/macros/cfg_select.stderr @@ -1,11 +1,5 @@ -error: expected `{`, found `1` - --> $DIR/cfg_select.rs:13:17 - | -LL | true => 1 - | ^ expected `{` - warning: unreachable predicate - --> $DIR/cfg_select.rs:20:5 + --> $DIR/cfg_select.rs:52:5 | LL | _ => {} | - always matches @@ -13,7 +7,7 @@ LL | true => {} | ^^^^ this predicate is never reached error: none of the predicates in this `cfg_select` evaluated to true - --> $DIR/cfg_select.rs:24:1 + --> $DIR/cfg_select.rs:56:1 | LL | / cfg_select! { LL | | @@ -22,10 +16,10 @@ LL | | } | |_^ error: none of the predicates in this `cfg_select` evaluated to true - --> $DIR/cfg_select.rs:29:1 + --> $DIR/cfg_select.rs:61:1 | LL | cfg_select! {} | ^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr index dfc983bf48744..e2aa5718cb650 100644 --- a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr +++ b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr @@ -10,7 +10,7 @@ LL | let _f = to_fn(|| test(i)); | | | captured by this `Fn` closure | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/moves-based-on-type-move-out-of-closure-env-issue-1965.rs:3:33 | LL | fn to_fn>(f: F) -> F { f } diff --git a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr index 7f9a8e50dae66..4749e3b8e453d 100644 --- a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr +++ b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr @@ -10,7 +10,7 @@ LL | expect_fn(|| drop(x.0)); | | | captured by this `Fn` closure | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/issue-52663-span-decl-captured-variable.rs:1:33 | LL | fn expect_fn(f: F) where F : Fn() { diff --git a/tests/ui/or-patterns/issue-64879-trailing-before-guard.fixed b/tests/ui/or-patterns/issue-64879-trailing-before-guard.fixed new file mode 100644 index 0000000000000..0c65f709d6636 --- /dev/null +++ b/tests/ui/or-patterns/issue-64879-trailing-before-guard.fixed @@ -0,0 +1,18 @@ +// In this regression test we check that a trailing `|` in an or-pattern just +// before the `if` token of a `match` guard will receive parser recovery with +// an appropriate error message. +//@ run-rustfix +#![allow(dead_code)] + +enum E { A, B } + +fn main() { + match E::A { + E::A | + E::B //~ ERROR a trailing `|` is not allowed in an or-pattern + if true => { + let _recovery_witness: i32 = 0i32; //~ ERROR mismatched types + } + _ => {} + } +} diff --git a/tests/ui/or-patterns/issue-64879-trailing-before-guard.rs b/tests/ui/or-patterns/issue-64879-trailing-before-guard.rs index 181c770096a5f..d7da564c2e1d6 100644 --- a/tests/ui/or-patterns/issue-64879-trailing-before-guard.rs +++ b/tests/ui/or-patterns/issue-64879-trailing-before-guard.rs @@ -1,6 +1,8 @@ // In this regression test we check that a trailing `|` in an or-pattern just // before the `if` token of a `match` guard will receive parser recovery with // an appropriate error message. +//@ run-rustfix +#![allow(dead_code)] enum E { A, B } @@ -9,7 +11,8 @@ fn main() { E::A | E::B | //~ ERROR a trailing `|` is not allowed in an or-pattern if true => { - let recovery_witness: bool = 0; //~ ERROR mismatched types + let _recovery_witness: i32 = 0u32; //~ ERROR mismatched types } + _ => {} } } diff --git a/tests/ui/or-patterns/issue-64879-trailing-before-guard.stderr b/tests/ui/or-patterns/issue-64879-trailing-before-guard.stderr index 91db3d049f629..238c76080dc4f 100644 --- a/tests/ui/or-patterns/issue-64879-trailing-before-guard.stderr +++ b/tests/ui/or-patterns/issue-64879-trailing-before-guard.stderr @@ -1,24 +1,24 @@ error: a trailing `|` is not allowed in an or-pattern - --> $DIR/issue-64879-trailing-before-guard.rs:10:14 + --> $DIR/issue-64879-trailing-before-guard.rs:12:14 | LL | E::A | | ---- while parsing this or-pattern starting here LL | E::B | | ^ + +error[E0308]: mismatched types + --> $DIR/issue-64879-trailing-before-guard.rs:14:42 | -help: remove the `|` +LL | let _recovery_witness: i32 = 0u32; + | --- ^^^^ expected `i32`, found `u32` + | | + | expected due to this | -LL - E::B | -LL + E::B +help: change the type of the numeric literal from `u32` to `i32` | - -error[E0308]: mismatched types - --> $DIR/issue-64879-trailing-before-guard.rs:12:42 +LL - let _recovery_witness: i32 = 0u32; +LL + let _recovery_witness: i32 = 0i32; | -LL | let recovery_witness: bool = 0; - | ---- ^ expected `bool`, found integer - | | - | expected due to this error: aborting due to 2 previous errors diff --git a/tests/ui/or-patterns/remove-leading-vert.fixed b/tests/ui/or-patterns/remove-leading-vert.fixed index 2851b8f18c54a..aa7975dc508aa 100644 --- a/tests/ui/or-patterns/remove-leading-vert.fixed +++ b/tests/ui/or-patterns/remove-leading-vert.fixed @@ -23,26 +23,26 @@ fn leading() { #[cfg(false)] fn trailing() { - let ( A ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern - let (a ,): (E,); //~ ERROR a trailing `|` is not allowed in an or-pattern - let ( A | B ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern - let [ A | B ]: [E; 1]; //~ ERROR a trailing `|` is not allowed in an or-pattern - let S { f: B }; //~ ERROR a trailing `|` is not allowed in an or-pattern - let ( A | B ): E; //~ ERROR unexpected token `||` in pattern + let ( A ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern + let (a,): (E,); //~ ERROR a trailing `|` is not allowed in an or-pattern + let ( A | B ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern + let [ A | B ]: [E; 1]; //~ ERROR a trailing `|` is not allowed in an or-pattern + let S { f: B }; //~ ERROR a trailing `|` is not allowed in an or-pattern + let ( A | B ): E; //~ ERROR unexpected token `||` in pattern //~^ ERROR a trailing `|` is not allowed in an or-pattern match A { - A => {} //~ ERROR a trailing `|` is not allowed in an or-pattern - A => {} //~ ERROR a trailing `|` is not allowed in an or-pattern - A | B => {} //~ ERROR unexpected token `||` in pattern + A => {} //~ ERROR a trailing `|` is not allowed in an or-pattern + A => {} //~ ERROR a trailing `||` is not allowed in an or-pattern + A | B => {} //~ ERROR unexpected token `||` in pattern //~^ ERROR a trailing `|` is not allowed in an or-pattern - | A | B => {} + | A | B => {} //~^ ERROR a trailing `|` is not allowed in an or-pattern } // These test trailing-vert in `let` bindings, but they also test that we don't emit a // duplicate suggestion that would confuse rustfix. - let a : u8 = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern - let a = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern - let a ; //~ ERROR a trailing `|` is not allowed in an or-pattern + let a : u8 = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern + let a = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern + let a ; //~ ERROR a trailing `|` is not allowed in an or-pattern } diff --git a/tests/ui/or-patterns/remove-leading-vert.rs b/tests/ui/or-patterns/remove-leading-vert.rs index 1e1dbfbc6e665..1b4eb669fbb45 100644 --- a/tests/ui/or-patterns/remove-leading-vert.rs +++ b/tests/ui/or-patterns/remove-leading-vert.rs @@ -32,7 +32,7 @@ fn trailing() { //~^ ERROR a trailing `|` is not allowed in an or-pattern match A { A | => {} //~ ERROR a trailing `|` is not allowed in an or-pattern - A || => {} //~ ERROR a trailing `|` is not allowed in an or-pattern + A || => {} //~ ERROR a trailing `||` is not allowed in an or-pattern A || B | => {} //~ ERROR unexpected token `||` in pattern //~^ ERROR a trailing `|` is not allowed in an or-pattern | A | B | => {} diff --git a/tests/ui/or-patterns/remove-leading-vert.stderr b/tests/ui/or-patterns/remove-leading-vert.stderr index 0323c64f04231..29450153ba459 100644 --- a/tests/ui/or-patterns/remove-leading-vert.stderr +++ b/tests/ui/or-patterns/remove-leading-vert.stderr @@ -3,12 +3,6 @@ error: function parameters require top-level or-patterns in parentheses | LL | fn fun1( | A: E) {} | ^^^ - | -help: remove the `|` - | -LL - fn fun1( | A: E) {} -LL + fn fun1( A: E) {} - | error: unexpected `||` before function parameter --> $DIR/remove-leading-vert.rs:12:14 @@ -78,12 +72,6 @@ LL | let ( A | ): E; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let ( A | ): E; -LL + let ( A ): E; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:27:12 @@ -92,12 +80,6 @@ LL | let (a |,): (E,); | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let (a |,): (E,); -LL + let (a ,): (E,); - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:28:17 @@ -106,12 +88,6 @@ LL | let ( A | B | ): E; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let ( A | B | ): E; -LL + let ( A | B ): E; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:29:17 @@ -120,12 +96,6 @@ LL | let [ A | B | ]: [E; 1]; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let [ A | B | ]: [E; 1]; -LL + let [ A | B ]: [E; 1]; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:30:18 @@ -134,12 +104,6 @@ LL | let S { f: B | }; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let S { f: B | }; -LL + let S { f: B }; - | error: unexpected token `||` in pattern --> $DIR/remove-leading-vert.rs:31:13 @@ -162,12 +126,6 @@ LL | let ( A || B | ): E; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let ( A || B | ): E; -LL + let ( A || B ): E; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:34:11 @@ -176,14 +134,8 @@ LL | A | => {} | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - A | => {} -LL + A => {} - | -error: a trailing `|` is not allowed in an or-pattern +error: a trailing `||` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:35:11 | LL | A || => {} @@ -192,11 +144,6 @@ LL | A || => {} | while parsing this or-pattern starting here | = note: alternatives in or-patterns are separated with `|`, not `||` -help: remove the `||` - | -LL - A || => {} -LL + A => {} - | error: unexpected token `||` in pattern --> $DIR/remove-leading-vert.rs:36:11 @@ -219,12 +166,6 @@ LL | A || B | => {} | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - A || B | => {} -LL + A || B => {} - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:38:17 @@ -233,12 +174,6 @@ LL | | A | B | => {} | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - | A | B | => {} -LL + | A | B => {} - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:45:11 @@ -247,12 +182,6 @@ LL | let a | : u8 = 0; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let a | : u8 = 0; -LL + let a : u8 = 0; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:46:11 @@ -261,12 +190,6 @@ LL | let a | = 0; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let a | = 0; -LL + let a = 0; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:47:11 @@ -275,12 +198,6 @@ LL | let a | ; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let a | ; -LL + let a ; - | error: aborting due to 21 previous errors diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr index 26731e29ffc57..5f4a5f31e34fb 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr +++ b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr @@ -5,12 +5,6 @@ LL | ! | | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - ! | -LL + ! - | error: a never pattern is always unreachable --> $DIR/ICE-130779-never-arm-no-oatherwise-block.rs:10:20 diff --git a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr index 8081f7b3a8b0f..f7750884b4a65 100644 --- a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -44,7 +44,7 @@ LL | LL | foo(f); | ^ move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait | - = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once help: consider cloning the value if the performance cost is acceptable | LL | foo(f.clone()); diff --git a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr index 132a31c8f7ceb..39c2fabf9eb14 100644 --- a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr +++ b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr @@ -12,7 +12,7 @@ LL | let X(_t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } @@ -37,7 +37,7 @@ LL | if let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } @@ -62,7 +62,7 @@ LL | while let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } @@ -90,7 +90,7 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } @@ -118,7 +118,7 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } @@ -143,7 +143,7 @@ LL | let X(mut _t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } @@ -168,7 +168,7 @@ LL | if let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } @@ -193,7 +193,7 @@ LL | while let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } @@ -221,7 +221,7 @@ LL | Either::One(mut _t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } @@ -249,7 +249,7 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } @@ -273,7 +273,7 @@ LL | let X(_t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:39:22 | LL | fn consume_fn(_f: F) { } @@ -298,7 +298,7 @@ LL | if let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:39:22 | LL | fn consume_fn(_f: F) { } @@ -323,7 +323,7 @@ LL | while let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:39:22 | LL | fn consume_fn(_f: F) { } @@ -351,7 +351,7 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:39:22 | LL | fn consume_fn(_f: F) { } @@ -379,7 +379,7 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:39:22 | LL | fn consume_fn(_f: F) { } @@ -404,7 +404,7 @@ LL | let X(mut _t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:39:22 | LL | fn consume_fn(_f: F) { } @@ -429,7 +429,7 @@ LL | if let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:39:22 | LL | fn consume_fn(_f: F) { } @@ -454,7 +454,7 @@ LL | while let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:39:22 | LL | fn consume_fn(_f: F) { } @@ -482,7 +482,7 @@ LL | Either::One(mut _t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:39:22 | LL | fn consume_fn(_f: F) { } @@ -510,7 +510,7 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:39:22 | LL | fn consume_fn(_f: F) { } @@ -534,7 +534,7 @@ LL | let X(_t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:50:29 | LL | fn method_consume_fn(&self, _f: F) { } @@ -559,7 +559,7 @@ LL | if let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:50:29 | LL | fn method_consume_fn(&self, _f: F) { } @@ -584,7 +584,7 @@ LL | while let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:50:29 | LL | fn method_consume_fn(&self, _f: F) { } @@ -612,7 +612,7 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:50:29 | LL | fn method_consume_fn(&self, _f: F) { } @@ -640,7 +640,7 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:50:29 | LL | fn method_consume_fn(&self, _f: F) { } @@ -665,7 +665,7 @@ LL | let X(mut _t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:50:29 | LL | fn method_consume_fn(&self, _f: F) { } @@ -690,7 +690,7 @@ LL | if let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:50:29 | LL | fn method_consume_fn(&self, _f: F) { } @@ -715,7 +715,7 @@ LL | while let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:50:29 | LL | fn method_consume_fn(&self, _f: F) { } @@ -743,7 +743,7 @@ LL | Either::One(mut _t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:50:29 | LL | fn method_consume_fn(&self, _f: F) { } @@ -771,7 +771,7 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:50:29 | LL | fn method_consume_fn(&self, _f: F) { } @@ -795,7 +795,7 @@ LL | let X(_t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } @@ -820,7 +820,7 @@ LL | if let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } @@ -845,7 +845,7 @@ LL | while let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } @@ -873,7 +873,7 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } @@ -901,7 +901,7 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } @@ -926,7 +926,7 @@ LL | let X(mut _t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } @@ -951,7 +951,7 @@ LL | if let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } @@ -976,7 +976,7 @@ LL | while let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } @@ -1004,7 +1004,7 @@ LL | Either::One(mut _t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } @@ -1032,7 +1032,7 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } @@ -1060,7 +1060,7 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } diff --git a/tests/ui/suggestions/option-content-move2.stderr b/tests/ui/suggestions/option-content-move2.stderr index c8aa6667b583f..5bcbdd711ae77 100644 --- a/tests/ui/suggestions/option-content-move2.stderr +++ b/tests/ui/suggestions/option-content-move2.stderr @@ -14,7 +14,7 @@ LL | LL | var = Some(NotCopyable); | --- variable moved due to use in closure | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/option-content-move2.rs:5:12 | LL | fn func H, H: FnMut()>(_: F) {} @@ -44,7 +44,7 @@ LL | LL | var = Some(NotCopyableButCloneable); | --- variable moved due to use in closure | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/option-content-move2.rs:5:12 | LL | fn func H, H: FnMut()>(_: F) {} diff --git a/tests/ui/suggestions/option-content-move3.stderr b/tests/ui/suggestions/option-content-move3.stderr index 2c9a86c036be4..f78d3cf678629 100644 --- a/tests/ui/suggestions/option-content-move3.stderr +++ b/tests/ui/suggestions/option-content-move3.stderr @@ -9,7 +9,7 @@ LL | move || { LL | let x = var; | ^^^ move occurs because `var` has type `NotCopyable`, which does not implement the `Copy` trait | - = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move3.rs:2:1 | @@ -38,7 +38,7 @@ LL | move || { LL | let x = var; | --- variable moved due to use in closure | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/option-content-move3.rs:6:12 | LL | fn func H, H: FnMut()>(_: F) {} @@ -63,7 +63,7 @@ LL | move || { LL | let x = var; | ^^^ move occurs because `var` has type `NotCopyableButCloneable`, which does not implement the `Copy` trait | - = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once help: consider borrowing here | LL | let x = &var; @@ -84,7 +84,7 @@ LL | move || { LL | let x = var; | --- variable moved due to use in closure | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/option-content-move3.rs:6:12 | LL | fn func H, H: FnMut()>(_: F) {} diff --git a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr index 9d87402a15bff..266da54941df2 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr @@ -10,7 +10,7 @@ LL | let f = to_fn(|| drop(x)); | | | captured by this `Fn` closure | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/unboxed-closure-illegal-move.rs:7:33 | LL | fn to_fn>(f: F) -> F { f } @@ -32,7 +32,7 @@ LL | let f = to_fn_mut(|| drop(x)); | | | captured by this `FnMut` closure | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/unboxed-closure-illegal-move.rs:8:37 | LL | fn to_fn_mut>(f: F) -> F { f } @@ -54,7 +54,7 @@ LL | let f = to_fn(move || drop(x)); | | | captured by this `Fn` closure | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/unboxed-closure-illegal-move.rs:7:33 | LL | fn to_fn>(f: F) -> F { f } @@ -72,7 +72,7 @@ LL | let f = to_fn_mut(move || drop(x)); | | | captured by this `FnMut` closure | -help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once --> $DIR/unboxed-closure-illegal-move.rs:8:37 | LL | fn to_fn_mut>(f: F) -> F { f }