diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index a36e7f3248dd0..dd5cf37c106e2 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -220,7 +220,7 @@ pub struct PathSegment { /// `None` means that no parameter list is supplied (`Path`), /// `Some` means that parameter list is supplied (`Path`) /// but it can be empty (`Path<>`). - /// `P` is used as a size optimization for the common case with no parameters. + /// `Box` is used as a size optimization for the common case with no parameters. pub args: Option>, } @@ -557,9 +557,10 @@ pub struct Crate { pub is_placeholder: bool, } -/// A semantic representation of a meta item. A meta item is a slightly -/// restricted form of an attribute -- it can only contain expressions in -/// certain leaf positions, rather than arbitrary token streams -- that is used +/// A semantic representation of a meta item. +/// +/// A meta item is a slightly restricted form of an attribute -- it can only contain +/// expressions in certain leaf positions, rather than arbitrary token streams -- that is used /// for most built-in attributes. /// /// E.g., `#[test]`, `#[derive(..)]`, `#[rustfmt::skip]` or `#[feature = "foo"]`. @@ -806,6 +807,7 @@ impl ByRef { } /// The mode of a binding (`mut`, `ref mut`, etc). +/// /// Used for both the explicit binding annotations given in the HIR for a binding /// and the final binding mode that we infer after type inference/match ergonomics. /// `.0` is the by-reference mode (`ref`, `ref mut`, or by value), @@ -1184,7 +1186,9 @@ impl UnOp { } } -/// A statement. No `attrs` or `tokens` fields because each `StmtKind` variant +/// A statement. +/// +/// No `attrs` or `tokens` fields because each `StmtKind` variant /// contains an AST node with those fields. (Except for `StmtKind::Empty`, /// which never has attrs or tokens) #[derive(Clone, Encodable, Decodable, Debug)] @@ -1375,6 +1379,8 @@ pub enum UnsafeSource { UserProvided, } +/// An anonymous constant. +/// /// A constant (expression) that's not an item or associated item, /// but needs its own `DefId` for type-checking, const-eval, etc. /// These are usually found nested inside types (e.g., array lengths) @@ -1962,8 +1968,9 @@ pub enum UnsafeBinderCastKind { Unwrap, } -/// The explicit `Self` type in a "qualified path". The actual -/// path, including the trait and the associated item, is stored +/// The explicit `Self` type in a "qualified path". +/// +/// The actual path, including the trait and the associated item, is stored /// separately. `position` represents the index of the associated /// item qualified with this `Self` type. /// @@ -3491,6 +3498,8 @@ pub struct AttrItem { pub span: Span, } +/// A synthetic attribute. +/// /// Synthetic attributes are inserted by the compiler. They cannot be written in source code, and /// so cannot be pretty-printed by the AST pretty printer (because its output should be valid Rust /// code). They receive special treatment because they must not affect observable language @@ -3513,7 +3522,7 @@ pub enum SyntheticAttr { /// evaluated true or not (or even failed to parse). The `pred` and `attrs` are not recorded /// because they are not needed. /// - /// The attribute is used by some clippy lints. + /// The attribute is used by rustdoc to display `doc_cfg` information and by some clippy lints. CfgAttrTrace(CfgEntry), } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index caa554c4aa669..34c7137b8676d 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -155,7 +155,10 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::attrs::EiiImpl { span: self.lower_span(*span), inner_span: self.lower_span(*inner_span), - impl_marked_unsafe: self.lower_safety(*impl_safety, hir::Safety::Safe).is_unsafe(), + impl_unsafe_span: match *impl_safety { + Safety::Unsafe(span) => Some(self.lower_span(span)), + Safety::Safe(_) | Safety::Default => None, + }, is_default: *is_default, resolution, } diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 15c4714624a63..28016aec48a50 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -305,7 +305,7 @@ impl AttributeParser for NakedParser { if other_attr.word_is(sym::target_feature) { if !cx.features().naked_functions_target_feature() { feature_err( - &cx.sess(), + cx.sess(), sym::naked_functions_target_feature, other_attr.span(), "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions", @@ -396,7 +396,7 @@ impl AttributeParser for UsedParser { Some(sym::compiler) => { if !cx.features().used_with_arg() { feature_err( - &cx.sess(), + cx.sess(), sym::used_with_arg, cx.attr_span, "`#[used(compiler)]` is currently unstable", @@ -408,7 +408,7 @@ impl AttributeParser for UsedParser { Some(sym::linker) => { if !cx.features().used_with_arg() { feature_err( - &cx.sess(), + cx.sess(), sym::used_with_arg, cx.attr_span, "`#[used(linker)]` is currently unstable", @@ -503,7 +503,7 @@ fn parse_tf_attribute( let Some(value_str) = cx.expect_string_literal(value) else { return features; }; - for feature in value_str.as_str().split(",") { + for feature in value_str.as_str().split(',') { features.push((Symbol::intern(feature), item.span())); } } @@ -602,7 +602,7 @@ impl SingleAttributeParser for InstrumentFnParser { const STABILITY: AttributeStability = unstable!(instrument_fn); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let instrument = match args { + match args { ArgParser::NameValue(nv) => match nv.value_as_str() { Some(sym::on) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::On)), Some(sym::off) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::Off)), @@ -621,8 +621,7 @@ impl SingleAttributeParser for InstrumentFnParser { cx.adcx().expected_specific_argument_strings(span, &[sym::on, sym::off]); None } - }; - instrument + } } } @@ -673,14 +672,7 @@ impl SingleAttributeParser for SanitizeParser { let is_on = match value.value_as_str() { Some(sym::on) => true, Some(sym::off) => false, - Some(_) => { - cx.adcx().expected_specific_argument_strings( - value.value_span, - &[sym::on, sym::off], - ); - return; - } - None => { + _ => { cx.adcx().expected_specific_argument_strings( value.value_span, &[sym::on, sym::off], @@ -737,7 +729,6 @@ impl SingleAttributeParser for SanitizeParser { sym::realtime, ], ); - continue; } } } @@ -809,9 +800,7 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { } for item in meta_item_list.mixed() { - let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else { - return None; - }; + let (ident, value) = cx.expect_name_value(item, item.span(), None)?; let attrib_to_write = match ident.name { sym::prefix_nops => { diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index d401aafed7bec..25835c1ed8b04 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -24,7 +24,7 @@ fn get( if let Some(value_str) = v.value_as_ident() { Some(value_str) } else { - cx.adcx().expected_string_literal(v.value_span, Some(&v.value_as_lit())); + cx.adcx().expected_string_literal(v.value_span, Some(v.value_as_lit())); None } } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 2486b5e3a65ed..a3a15978d2a37 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -49,7 +49,7 @@ pub(crate) enum Mode { } impl Mode { - fn as_str(&self) -> &'static str { + fn as_str(self) -> &'static str { match self { Self::RustcOnUnimplemented => "rustc_on_unimplemented", Self::DiagnosticOnUnimplemented => "diagnostic::on_unimplemented", @@ -61,7 +61,7 @@ impl Mode { } } - fn expected_options(&self) -> &'static str { + fn expected_options(self) -> &'static str { const DEFAULT: &str = "at least one of the `message`, `note` and `label` options are expected"; const DIAGNOSTIC_ON_TYPE_ERROR_EXPECTED_OPTIONS: &str = @@ -70,16 +70,16 @@ impl Mode { Self::RustcOnUnimplemented => { "see " } - Self::DiagnosticOnUnimplemented => DEFAULT, - Self::DiagnosticOnConst => DEFAULT, - Self::DiagnosticOnMove => DEFAULT, - Self::DiagnosticOnUnknown => DEFAULT, - Self::DiagnosticOnUnmatchedArgs => DEFAULT, + Self::DiagnosticOnUnimplemented + | Self::DiagnosticOnConst + | Self::DiagnosticOnMove + | Self::DiagnosticOnUnknown + | Self::DiagnosticOnUnmatchedArgs => DEFAULT, Self::DiagnosticOnTypeError => DIAGNOSTIC_ON_TYPE_ERROR_EXPECTED_OPTIONS, } } - fn allowed_options(&self) -> &'static str { + fn allowed_options(self) -> &'static str { const DEFAULT: &str = "only `message`, `note` and `label` are allowed as options"; const DIAGNOSTIC_ON_TYPE_ERROR_ALLOWED_OPTIONS: &str = "only `note` is allowed as option for `diagnostic::on_type_error`"; @@ -87,16 +87,16 @@ impl Mode { Self::RustcOnUnimplemented => { "see " } - Self::DiagnosticOnUnimplemented => DEFAULT, - Self::DiagnosticOnConst => DEFAULT, - Self::DiagnosticOnMove => DEFAULT, - Self::DiagnosticOnUnknown => DEFAULT, - Self::DiagnosticOnUnmatchedArgs => DEFAULT, + Self::DiagnosticOnUnimplemented + | Self::DiagnosticOnConst + | Self::DiagnosticOnMove + | Self::DiagnosticOnUnknown + | Self::DiagnosticOnUnmatchedArgs => DEFAULT, Self::DiagnosticOnTypeError => DIAGNOSTIC_ON_TYPE_ERROR_ALLOWED_OPTIONS, } } - fn allowed_format_arguments(&self) -> &'static str { + fn allowed_format_arguments(self) -> &'static str { match self { Self::RustcOnUnimplemented => { "see for allowed format arguments" @@ -356,12 +356,11 @@ fn parse_directive_items<'p>( if is_root { let items = or_malformed!(item.args().as_list()?); let mut iter = items.mixed(); - let filter: &MetaItemOrLitParser = match iter.next() { - Some(c) => c, - None => { - cx.emit_err(InvalidOnClause::Empty { span }); - continue; - } + let filter = if let Some(c) = iter.next() { + c + } else { + cx.emit_err(InvalidOnClause::Empty { span }); + continue; }; let filter = parse_filter(filter); diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs index 18f980bfebbe8..df8cee63506cc 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs @@ -17,6 +17,8 @@ impl AttributeParser for OnUnmatchedArgsParser { AttributeStability::Stable, // Unstable, stability checked manually in the parser |this, cx, args| { if !cx.features().diagnostic_on_unmatched_args() { + // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs + args.ignore_args(); return; } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs index 0a18b69dda0ed..64c5ed704f8c5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs @@ -45,7 +45,7 @@ impl AttributeParser for OpaqueParser { AllowedTargets::AllowListWarnRest(&[Allow(Target::MacroDef)]); fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { - if let Some(_) = self.attr_span { Some(AttributeKind::Opaque) } else { None } + if self.attr_span.is_some() { Some(AttributeKind::Opaque) } else { None } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index d3b632fae99d9..45d93e215c781 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -50,13 +50,13 @@ impl SingleAttributeParser for InlineParser { } _ => { cx.adcx().expected_specific_argument(l.span(), &[sym::always, sym::never]); - return None; + None } } } ArgParser::NameValue(_) => { cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); - return None; + None } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 07713590ff2e9..935265924d7da 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -144,13 +144,13 @@ impl CombineAttributeParser for LinkParser { let mut verbatim = None; if let Some((modifiers, span)) = modifiers { for modifier in modifiers.as_str().split(',') { - let (modifier, value): (Symbol, bool) = match modifier.strip_prefix(&['+', '-']) { - Some(m) => (Symbol::intern(m), modifier.starts_with('+')), - None => { + let (modifier, value): (Symbol, bool) = + if let Some(m) = modifier.strip_prefix(['+', '-']) { + (Symbol::intern(m), modifier.starts_with('+')) + } else { cx.emit_err(InvalidLinkModifier { span }); continue; - } - }; + }; macro report_unstable_modifier($feature: ident) { if !features.$feature() { @@ -196,9 +196,14 @@ impl CombineAttributeParser for LinkParser { cx.emit_err(WholeArchiveNeedsStatic { span }); } - (sym::as_dash_needed, Some(NativeLibKind::Dylib { as_needed })) - | (sym::as_dash_needed, Some(NativeLibKind::Framework { as_needed })) - | (sym::as_dash_needed, Some(NativeLibKind::RawDylib { as_needed })) => { + ( + sym::as_dash_needed, + Some( + NativeLibKind::Dylib { as_needed } + | NativeLibKind::Framework { as_needed } + | NativeLibKind::RawDylib { as_needed }, + ), + ) => { report_unstable_modifier!(native_link_modifiers_as_needed); assign_modifier(as_needed) } @@ -642,14 +647,13 @@ pub(crate) struct LinkageParser; impl SingleAttributeParser for LinkageParser { const PATH: &[Symbol] = &[sym::linkage]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ - Allow(Target::Fn), + Allow(Target::Fn), // const fn denied in check_attr Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::TraitImpl)), Allow(Target::Static), - Allow(Target::ForeignStatic), + Allow(Target::ForeignStatic), // extern static mut denied in check_attr Allow(Target::ForeignFn), - Warn(Target::Method(MethodKind::Trait { body: false })), // Not inherited ]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: [ "available_externally", @@ -667,9 +671,7 @@ impl SingleAttributeParser for LinkageParser { fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { let name_value = cx.expect_name_value(args, cx.attr_span, Some(sym::linkage))?; - let Some(value) = cx.expect_string_literal(name_value) else { - return None; - }; + let value = cx.expect_string_literal(name_value)?; // Use the names from src/llvm/docs/LangRef.rst here. Most types are only // applicable to variable declarations and may not really make sense for diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index f49a7e674d560..17123f67cf276 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -149,12 +149,12 @@ impl SingleAttributeParser for MacroExportParser { cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS); return None; }; - match l.meta_item_no_args().and_then(|i| i.path().word_sym()) { - Some(sym::local_inner_macros) => true, - _ => { - cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS); - return None; - } + if l.meta_item_no_args().is_some_and(|m| m.path().word_is(sym::local_inner_macros)) + { + true + } else { + cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS); + return None; } } ArgParser::NameValue(nv) => { diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 0adae406df5c6..885b35d353ef7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -247,7 +247,7 @@ impl OnDuplicate { this: unused, other: used, name: Symbol::intern( - &P::PATH.into_iter().map(|i| i.to_string()).collect::>().join(".."), + &P::PATH.iter().map(|i| i.to_string()).collect::>().join(".."), ), }); } diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index dfb7ce5e7b2f4..bb0da73df015c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -59,7 +59,7 @@ impl CombineAttributeParser for ReprParser { cx.adcx().expected_identifier(param.span()); continue; }; - reprs.extend(parse_repr(cx, &item).map(|r| (r, param.span()))); + reprs.extend(parse_repr(cx, item).map(|r| (r, param.span()))); } reprs } @@ -144,7 +144,7 @@ fn parse_repr(cx: &mut AcceptContext<'_, '_>, param: &MetaItemParser) -> Option< Some(sym::simd) => { if cx.features.is_some_and(|feats| !feats.repr_simd()) { feature_err( - &cx.sess(), + cx.sess(), sym::repr_simd, param.span(), "SIMD types are experimental and possibly buggy", diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 0cfb68d523f9b..1ff0cb26ac8d9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -309,10 +309,10 @@ pub(crate) fn parse_stability( let word = param.path().word(); match word.map(|i| i.name) { Some(sym::feature) => { - insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())? + insert_value_into_option_or_error(cx, param, &mut feature, word.unwrap())? } Some(sym::since) => { - insert_value_into_option_or_error(cx, ¶m, &mut since, word.unwrap())? + insert_value_into_option_or_error(cx, param, &mut since, word.unwrap())? } _ => { cx.adcx().expected_specific_argument(param_span, &[sym::feature, sym::since]); @@ -376,13 +376,13 @@ pub(crate) fn parse_unstability( let word = param.path().word(); match word.map(|i| i.name) { Some(sym::feature) => { - insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())? + insert_value_into_option_or_error(cx, param, &mut feature, word.unwrap())? } Some(sym::reason) => { - insert_value_into_option_or_error(cx, ¶m, &mut reason, word.unwrap())? + insert_value_into_option_or_error(cx, param, &mut reason, word.unwrap())? } Some(sym::issue) => { - insert_value_into_option_or_error(cx, ¶m, &mut issue, word.unwrap())?; + insert_value_into_option_or_error(cx, param, &mut issue, word.unwrap())?; // These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item // is a name/value pair string literal. @@ -406,10 +406,10 @@ pub(crate) fn parse_unstability( }; } Some(sym::implied_by) => { - insert_value_into_option_or_error(cx, ¶m, &mut implied_by, word.unwrap())? + insert_value_into_option_or_error(cx, param, &mut implied_by, word.unwrap())? } Some(sym::old_name) => { - insert_value_into_option_or_error(cx, ¶m, &mut old_name, word.unwrap())? + insert_value_into_option_or_error(cx, param, &mut old_name, word.unwrap())? } _ => { cx.adcx().expected_specific_argument( @@ -493,10 +493,10 @@ impl CombineAttributeParser for UnstableRemovedParser { return None; }; match word.name { - sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature, word)?, - sym::since => insert_value_into_option_or_error(cx, ¶m, &mut since, word)?, - sym::reason => insert_value_into_option_or_error(cx, ¶m, &mut reason, word)?, - sym::link => insert_value_into_option_or_error(cx, ¶m, &mut link, word)?, + sym::feature => insert_value_into_option_or_error(cx, param, &mut feature, word)?, + sym::since => insert_value_into_option_or_error(cx, param, &mut since, word)?, + sym::reason => insert_value_into_option_or_error(cx, param, &mut reason, word)?, + sym::link => insert_value_into_option_or_error(cx, param, &mut link, word)?, _ => { cx.adcx().expected_specific_argument( param.span(), diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index a2348eef975cf..69bdccb85c5cd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -53,7 +53,7 @@ pub(crate) struct RustcParenSugarParser; impl NoArgsAttributeParser for RustcParenSugarParser { const PATH: &[Symbol] = &[sym::rustc_paren_sugar]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); - const STABILITY: AttributeStability = unstable!(rustc_attrs); + const STABILITY: AttributeStability = unstable!(unboxed_closures); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcParenSugar; } diff --git a/compiler/rustc_attr_parsing/src/attributes/unroll.rs b/compiler/rustc_attr_parsing/src/attributes/unroll.rs index 50c73ca041cdd..3438fc044ec55 100644 --- a/compiler/rustc_attr_parsing/src/attributes/unroll.rs +++ b/compiler/rustc_attr_parsing/src/attributes/unroll.rs @@ -47,7 +47,7 @@ impl SingleAttributeParser for UnrollParser { ArgParser::NameValue(_) => { let inner_span = cx.inner_span; cx.adcx().expected_list_or_no_args(inner_span); - return None; + None } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 47a8ac4ce51a8..b58489712fa52 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -12,7 +12,7 @@ use crate::session_diagnostics::LimitInvalid; /// Parse a rustc version number written inside string literal in an attribute, /// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are -/// not accepted in this position, unlike when parsing CFG_RELEASE. +/// not accepted in this position, unlike when parsing `CFG_RELEASE`. pub fn parse_version(s: Symbol) -> Option { let mut components = s.as_str().split('-'); let d = components.next()?; @@ -70,7 +70,7 @@ impl AcceptContext<'_, '_> { IntErrorKind::Zero => { panic!("zero is a valid `limit` so should have returned Ok() when parsing") } - kind => panic!("unimplemented IntErrorKind variant: {:?}", kind), + kind => panic!("unimplemented IntErrorKind variant: {kind:?}"), }, }; diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index ed926330ed289..c4b2a5b509051 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -660,9 +660,8 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { // don't `uninterpolate` the token to avoid suggesting anything butchered or questionable // when macro metavariables are involved. let snapshot = self.parser.create_snapshot_for_diagnostic(); - let stmt = self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false); - match stmt { - Ok(Some(stmt)) => { + match self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false) { + Ok(stmt) => { // The user tried to write something like // `#[deprecated(note = concat!("a", "b"))]`. err.descr = stmt.kind.descr().to_string(); @@ -692,7 +691,6 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { }); } } - Ok(None) => {} Err(e) => { e.cancel(); self.parser.restore_snapshot(snapshot); diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 60854d67ec946..2fb89939181e6 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -126,9 +126,8 @@ impl CfgEval<'_> { Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap()) } Annotatable::Stmt(_) => { - let stmt = parser - .parse_stmt_without_recovery(false, ForceCollect::Yes, false)? - .unwrap(); + let stmt = + parser.parse_stmt_without_recovery(false, ForceCollect::Yes, false)?; Annotatable::Stmt(Box::new(self.flat_map_stmt(stmt).pop().unwrap())) } Annotatable::Expr(_) => { diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 691b13f03e965..0f9c3c8534785 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -198,16 +198,10 @@ fn process_builtin_attrs( codegen_fn_attrs.import_linkage = linkage; if tcx.is_mutable_static(did.into()) { - let mut diag = tcx.dcx().struct_span_err( + tcx.dcx().span_delayed_bug( *span, - "extern mutable statics are not allowed with `#[linkage]`", + "`extern { #[linkage] static mut ...` is checked in check_attr}", ); - diag.note( - "marking the extern static mutable would allow changing which \ - symbol the static references rather than make the target of the \ - symbol mutable", - ); - diag.emit(); } } else { codegen_fn_attrs.linkage = linkage; diff --git a/compiler/rustc_data_structures/src/svh.rs b/compiler/rustc_data_structures/src/svh.rs index 2c4e00c824d80..67594f6dae79d 100644 --- a/compiler/rustc_data_structures/src/svh.rs +++ b/compiler/rustc_data_structures/src/svh.rs @@ -11,17 +11,8 @@ use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash}; use crate::fingerprint::Fingerprint; -#[derive( - Copy, - Clone, - PartialEq, - Eq, - Debug, - Encodable_NoContext, - Decodable_NoContext, - Hash, - StableHash -)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Encodable_NoContext, Decodable_NoContext, StableHash)] pub struct Svh { hash: Fingerprint, } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 0c775e22ccf23..4f55e0506e799 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1120,9 +1120,7 @@ pub fn parse_ast_fragment<'a>( let mut stmts = SmallVec::new(); // Won't make progress on a `}`. while this.token != token::Eof && this.token != token::CloseBrace { - if let Some(stmt) = this.parse_full_stmt(AttemptLocalParseRecovery::Yes)? { - stmts.push(stmt); - } + stmts.push(this.parse_full_stmt(AttemptLocalParseRecovery::Yes)?); } AstFragment::Stmts(stmts) } diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index 827538f58f980..b4b9db5d93f48 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -6,10 +6,10 @@ macro_rules! arena_types { $macro!([ // HIR types [] asm_template: rustc_ast::InlineAsmTemplatePiece, - [] attribute: rustc_hir::Attribute, - [] owner_info: rustc_hir::OwnerInfo<'tcx>, + [] attribute: crate::Attribute, + [] owner_info: crate::OwnerInfo<'tcx>, [] macro_def: rustc_ast::MacroDef, - [] delegation_info: rustc_hir::DelegationInfo, + [] delegation_info: crate::DelegationInfo, ]); ) } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index db492475a3aa4..bb0d7227bd171 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -12,7 +12,6 @@ use rustc_ast::{AttrStyle, Path, ast}; use rustc_data_structures::Limit; use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; -use rustc_hir::LangItem; use rustc_macros::{Decodable, Encodable, PrintAttribute, StableHash}; use rustc_span::def_id::DefId; use rustc_span::hygiene::Transparency; @@ -22,7 +21,7 @@ use thin_vec::ThinVec; use crate::attrs::diagnostic::*; use crate::attrs::pretty_printing::PrintAttribute; -use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability}; +use crate::{DefaultBodyStability, LangItem, PartialConstStability, RustcVersion, Stability}; #[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub enum EiiImplResolution { @@ -40,7 +39,7 @@ pub enum EiiImplResolution { #[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub struct EiiImpl { pub resolution: EiiImplResolution, - pub impl_marked_unsafe: bool, + pub impl_unsafe_span: Option, pub span: Span, pub inner_span: Span, pub is_default: bool, @@ -370,19 +369,8 @@ pub enum PeImportNameType { Undecorated, } -#[derive( - Copy, - Clone, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Encodable, - Decodable, - PrintAttribute -)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Encodable, Decodable, PrintAttribute)] #[derive(StableHash)] pub enum NativeLibKind { /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) diff --git a/compiler/rustc_hir/src/attrs/mod.rs b/compiler/rustc_hir/src/attrs/mod.rs index b3c1deaa48fa8..1e863a71640c4 100644 --- a/compiler/rustc_hir/src/attrs/mod.rs +++ b/compiler/rustc_hir/src/attrs/mod.rs @@ -85,13 +85,13 @@ macro_rules! find_attr { 'done: { for i in $attributes_list { #[allow(unused_imports)] - use rustc_hir::attrs::AttributeKind::*; - let i: &rustc_hir::Attribute = i; + use $crate::attrs::AttributeKind::*; + let i: &$crate::Attribute = i; match i { - rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => { + $crate::Attribute::Parsed($pattern) $(if $guard)? => { break 'done Some($e); } - rustc_hir::Attribute::Unparsed(..) => {} + $crate::Attribute::Unparsed(..) => {} // In lint emitting, there's a specific exception for this warning. // It's not usually emitted from inside macros from other crates // (see https://github.com/rust-lang/rust/issues/110613) diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 96e89a8d8cdb6..d1c4e158a7959 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -114,71 +114,65 @@ pub trait HirTyCtxt<'hir> { fn hir_foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir>; } -// Used when no tcx is actually available, forcing manual implementation of nested visitors. +/// Used when no tcx is actually available, forcing manual implementation of nested visitors. impl<'hir> HirTyCtxt<'hir> for ! { fn hir_node(&self, _: HirId) -> Node<'hir> { - unreachable!(); + *self } fn hir_body(&self, _: BodyId) -> &'hir Body<'hir> { - unreachable!(); + *self } fn hir_item(&self, _: ItemId) -> &'hir Item<'hir> { - unreachable!(); + *self } fn hir_trait_item(&self, _: TraitItemId) -> &'hir TraitItem<'hir> { - unreachable!(); + *self } fn hir_impl_item(&self, _: ImplItemId) -> &'hir ImplItem<'hir> { - unreachable!(); + *self } fn hir_foreign_item(&self, _: ForeignItemId) -> &'hir ForeignItem<'hir> { - unreachable!(); + *self } } -pub mod nested_filter { - use super::HirTyCtxt; - - /// Specifies what nested things a visitor wants to visit. By "nested - /// things", we are referring to bits of HIR that are not directly embedded - /// within one another but rather indirectly, through a table in the crate. - /// This is done to control dependencies during incremental compilation: the - /// non-inline bits of HIR can be tracked and hashed separately. - /// - /// The most common choice is `OnlyBodies`, which will cause the visitor to - /// visit fn bodies for fns that it encounters, and closure bodies, but - /// skip over nested item-like things. - /// - /// See the comments at [`rustc_hir::intravisit`] for more details on the overall - /// visit strategy. - pub trait NestedFilter<'hir> { - type MaybeTyCtxt: HirTyCtxt<'hir>; - - /// Whether the visitor visits nested "item-like" things. - /// E.g., item, impl-item. - const INTER: bool; - /// Whether the visitor visits "intra item-like" things. - /// E.g., function body, closure, `AnonConst` - const INTRA: bool; - } - - /// Do not visit any nested things. When you add a new - /// "non-nested" thing, you will want to audit such uses to see if - /// they remain valid. - /// - /// Use this if you are only walking some particular kind of tree - /// (i.e., a type, or fn signature) and you don't want to thread a - /// `tcx` around. - pub struct None(()); - impl NestedFilter<'_> for None { - type MaybeTyCtxt = !; - const INTER: bool = false; - const INTRA: bool = false; - } +/// Specifies what nested things a visitor wants to visit. By "nested +/// things", we are referring to bits of HIR that are not directly embedded +/// within one another but rather indirectly, through a table in the crate. +/// This is done to control dependencies during incremental compilation: the +/// non-inline bits of HIR can be tracked and hashed separately. +/// +/// The most common choice is `OnlyBodies`, which will cause the visitor to +/// visit fn bodies for fns that it encounters, and closure bodies, but +/// skip over nested item-like things. +/// +/// See the [module level documentation](self) for more details on the overall +/// visit strategy. +pub trait NestedFilter<'hir> { + type MaybeTyCtxt: HirTyCtxt<'hir>; + + /// Whether the visitor visits nested "item-like" things. + /// E.g., item, impl-item. + const INTER: bool; + /// Whether the visitor visits "intra item-like" things. + /// E.g., function body, closure, `AnonConst` + const INTRA: bool; +} + +/// Do not visit any nested things. When you add a new +/// "non-nested" thing, you will want to audit such uses to see if +/// they remain valid. +/// +/// Use this if you are only walking some particular kind of tree +/// (i.e., a type, or fn signature) and you don't want to thread a +/// `tcx` around. +pub struct IgnoreNested(()); +impl NestedFilter<'_> for IgnoreNested { + type MaybeTyCtxt = !; + const INTER: bool = false; + const INTRA: bool = false; } -use nested_filter::NestedFilter; - /// Each method of the Visitor trait is a hook to be potentially /// overridden. Each method's default implementation recursively visits /// the substructure of the input via the corresponding `walk` method; @@ -215,7 +209,7 @@ pub trait Visitor<'v>: Sized { /// `visit_nested_XXX` methods. If a new `visit_nested_XXX` variant is /// added in the future, it will cause a panic which can be detected /// and fixed appropriately. - type NestedFilter: NestedFilter<'v> = nested_filter::None; + type NestedFilter: NestedFilter<'v> = IgnoreNested; /// The result type of the `visit_*` methods. Can be either `()`, /// or `ControlFlow`. @@ -226,16 +220,16 @@ pub trait Visitor<'v>: Sized { fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { panic!( "maybe_tcx must be implemented or consider using \ - `type NestedFilter = nested_filter::None` (the default)" + `type NestedFilter = Skip` (the default)" ); } /// Invoked when a nested item is encountered. By default, when - /// `Self::NestedFilter` is `nested_filter::None`, this method does + /// `Self::NestedFilter` is `Skip`, this method does /// nothing. **You probably don't want to override this method** -- /// instead, override [`Self::NestedFilter`] or use the "shallow" or /// "deep" visit patterns described at - /// [`rustc_hir::intravisit`]. The only reason to override + /// [`intravisit`](self). The only reason to override /// this method is if you want a nested pattern but cannot supply a /// `TyCtxt`; see `maybe_tcx` for advice. fn visit_nested_item(&mut self, id: ItemId) -> Self::Result { diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 9b976d21d5365..4e3b27e0b2442 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -15,8 +15,6 @@ #![recursion_limit = "256"] // tidy-alphabetical-end -extern crate self as rustc_hir; - mod arena; pub mod attrs; pub mod canonical_symbols; diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 605b6e3751ef3..6c393ed3dfc6b 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -947,9 +947,6 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { let attrs = tcx.get_all_attrs(def_id); let paren_sugar = find_attr!(attrs, RustcParenSugar); - if paren_sugar && !tcx.features().unboxed_closures() { - tcx.dcx().emit_err(diagnostics::ParenSugarAttribute { span: item.span }); - } // Only regular traits can be marker. let is_marker = !is_alias && find_attr!(attrs, Marker); diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index a6e274ceb4bc6..0ea353cf14cee 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -862,16 +862,6 @@ pub(crate) struct EnumDiscriminantOverflowed { pub wrapped_discr: String, } -#[derive(Diagnostic)] -#[diag( - "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation" -)] -#[help("add `#![feature(unboxed_closures)]` to the crate attributes to use it")] -pub(crate) struct ParenSugarAttribute { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("use of SIMD type{$snip} in FFI is highly experimental and may result in invalid code")] #[help("add `#![feature(simd_ffi)]` to the crate attributes to enable")] diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index b084b412417c5..d7852fc0e4f69 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -840,7 +840,7 @@ pub(crate) enum RedefiningRuntimeSymbolsDiag<'tcx> { "invalid definition of the runtime `{$symbol_name}` symbol used by the standard library" )] #[note( - "expected `{$expected_fn_sig}` + "expected `{$expected_fn_sig}` (for the current target) found `{$found_fn_sig}`" )] #[help( @@ -851,7 +851,7 @@ pub(crate) enum RedefiningRuntimeSymbolsDiag<'tcx> { "suspicious definition of the runtime `{$symbol_name}` symbol used by the standard library" )] #[note( - "expected `{$expected_fn_sig}` + "expected `{$expected_fn_sig}` (for the current target) found `{$found_fn_sig}`" )] #[help( diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 6949090781cf6..767fa647612af 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -149,9 +149,8 @@ impl From for LintExpectationId { /// Setting for how to handle a lint. /// /// See: -#[derive( - Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable, StableHash -)] +#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +#[derive(Encodable, Decodable, StableHash)] pub enum Level { /// The `allow` level will not issue any message. Allow, diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 391bd41be828c..f460fd42cb31c 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -217,9 +217,8 @@ pub struct DepKindVTable<'tcx> { /// some independent path or string that persists between runs without /// the need to be mapped or unmapped. (This ensures we can serialize /// them even in the absence of a tcx.) -#[derive( - Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, StableHash -)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Encodable, Decodable, StableHash)] pub struct WorkProductId { hash: Fingerprint, } diff --git a/compiler/rustc_middle/src/hir/nested_filter.rs b/compiler/rustc_middle/src/hir/nested_filter.rs index e18c28a8eac01..304af22a87ea7 100644 --- a/compiler/rustc_middle/src/hir/nested_filter.rs +++ b/compiler/rustc_middle/src/hir/nested_filter.rs @@ -1,4 +1,4 @@ -use rustc_hir::intravisit::nested_filter::NestedFilter; +use rustc_hir::intravisit::NestedFilter; use crate::ty::TyCtxt; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 9798138b48b80..94d3c7dccfc6c 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3331,13 +3331,9 @@ impl<'a> Parser<'a> { self.restore_snapshot(pre_pat_snapshot); match self.parse_stmt_without_recovery(true, ForceCollect::No, false) { // Consume statements for as long as possible. - Ok(Some(stmt)) => { + Ok(stmt) => { stmts.push(stmt); } - Ok(None) => { - self.restore_snapshot(start_snapshot); - break; - } // We couldn't parse either yet another statement missing it's // enclosing block nor the next arm's pattern or closing brace. Err(stmt_err) => { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index fc539405249a6..ef62316abf941 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -987,12 +987,9 @@ impl<'a> Parser<'a> { let initial_semicolon = self.token.span; while self.eat(exp!(Semi)) { - let _ = self - .parse_stmt_without_recovery(false, ForceCollect::No, false) - .unwrap_or_else(|e| { - e.cancel(); - None - }); + if let Err(e) = self.parse_stmt_without_recovery(false, ForceCollect::No, false) { + e.cancel(); + } } expect_err diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index e4602abf7779f..9f9545c194082 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -139,7 +139,7 @@ impl<'a> Parser<'a> { this.parse_block().map(|block| WithTokens::new(block)) })?)) } - NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? { + NonterminalKind::Stmt => match self.parse_stmt_nonterminal(ForceCollect::Yes) { Some(stmt) => Ok(ParseNtResult::Stmt(Box::new(stmt))), None => { Err(self.dcx().create_err(UnexpectedNonterminal::Statement(self.token.span))) diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 27a047f598513..6dddc8b2407dc 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -27,18 +27,22 @@ use crate::diagnostics::{self, MalformedLoopLabel}; use crate::exp; impl<'a> Parser<'a> { - /// Parses a statement. This stops just before trailing semicolons on everything but items. + /// Parses a statement nonterminal, which has a peculiar syntax preserved for backward + /// compatibility. The parsing stops just before trailing semicolons on everything but items. /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. /// /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of /// whether or not we have attributes. // Public for rustfmt usage. - pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option> { - Ok(self.parse_stmt_without_recovery(false, force_collect, false).unwrap_or_else(|e| { - e.emit(); - self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); - None - })) + pub fn parse_stmt_nonterminal(&mut self, force_collect: ForceCollect) -> Option { + match self.parse_stmt_without_recovery(false, force_collect, false) { + Ok(stmt) => Some(stmt), + Err(e) => { + e.emit(); + self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); + None + } + } } /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of @@ -49,19 +53,18 @@ impl<'a> Parser<'a> { capture_semi: bool, force_collect: ForceCollect, force_full_expr: bool, - ) -> PResult<'a, Option> { + ) -> PResult<'a, Stmt> { let pre_attr_pos = self.collect_pos(); let attrs = self.parse_outer_attributes()?; let lo = self.token.span; - if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| { + if let Some(mut stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| { this.parse_stmt_without_recovery(false, ForceCollect::Yes, false) }) { - let mut stmt = stmt.expect("an actual statement"); stmt.visit_attrs(|stmt_attrs| { attrs.prepend_to_nt_inner(stmt_attrs); }); - return Ok(Some(stmt)); + return Ok(stmt); } if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) { @@ -161,9 +164,12 @@ impl<'a> Parser<'a> { self.mk_stmt(lo.to(item.span), StmtKind::Item(Box::new(item))) } else if self.eat(exp!(Semi)) { // Do not attempt to parse an expression if we're done here. - self.error_outer_attrs(attrs); + self.error_outer_attrs(attrs)?; self.mk_stmt(lo, StmtKind::Empty) - } else if self.token != token::CloseBrace { + } else if self.token == token::CloseBrace { + self.error_outer_attrs(attrs)?; + self.dcx().span_bug(self.token.span, "don't parse a statement if you see `}`"); + } else { // Remainder are line-expr stmts. This is similar to the `parse_stmt_path_start` case // above. let restrictions = @@ -185,13 +191,10 @@ impl<'a> Parser<'a> { .emit_err(diagnostics::AssignmentElseNotAllowed { span: e.span.to(bl.span) }); } self.mk_stmt(lo.to(e.span), StmtKind::Expr(e)) - } else { - self.error_outer_attrs(attrs); - return Ok(None); }; self.maybe_augment_stashed_expr_in_pats_with_suggestions(&stmt); - Ok(Some(stmt)) + Ok(stmt) } fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> { @@ -272,20 +275,21 @@ impl<'a> Parser<'a> { /// Error on outer attributes in this context. /// Also error if the previous token was a doc comment. - fn error_outer_attrs(&self, attrs: AttrWrapper) { - if !attrs.is_empty() - && let attrs @ [.., last] = &*attrs.take_for_recovery(self.psess) - { - if last.is_doc_comment() { - self.dcx().emit_err(diagnostics::DocCommentDoesNotDocumentAnything { - span: last.span, - missing_comma: None, - }); - } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) { - self.dcx() - .emit_err(diagnostics::ExpectedStatementAfterOuterAttr { span: last.span }); - } + fn error_outer_attrs(&self, attrs: AttrWrapper) -> PResult<'a, ()> { + if attrs.is_empty() { + return Ok(()); } + let attrs = attrs.take_for_recovery(self.psess); + let last = attrs.last().unwrap(); + Err(if last.is_doc_comment() { + self.dcx().create_err(diagnostics::DocCommentDoesNotDocumentAnything { + span: last.span, + missing_comma: None, + }) + } else { + assert_eq!(last.style, AttrStyle::Outer); + self.dcx().create_err(diagnostics::ExpectedStatementAfterOuterAttr { span: last.span }) + }) } fn recover_stmt_local_after_let( @@ -521,6 +525,10 @@ impl<'a> Parser<'a> { let sp = self.token.span; let mut err = self.dcx().struct_span_err(sp, msg); self.label_expected_raw_ref(&mut err); + err.span_label(sp, "expected `{`"); + if self.token == token::CloseBrace { + return err; + } let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon @@ -547,13 +555,13 @@ impl<'a> Parser<'a> { // since we want to protect against: // `if 1 1 + 1 {` being suggested as `if { 1 } 1 + 1 {` // + + - Ok(Some(_)) + Ok(_) if (!self.token.is_keyword(kw::Else) && self.look_ahead(1, |t| t == &token::OpenBrace)) || do_not_suggest_help => {} // Do not suggest `if foo println!("") {;}` (as would be seen in test for #46836). - Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {} - Ok(Some(stmt)) => { + Ok(Stmt { kind: StmtKind::Empty, .. }) => {} + Ok(stmt) => { let stmt_own_line = self.psess.source_map().is_line_before_span_empty(sp); let stmt_span = if stmt_own_line && self.eat(exp!(Semi)) { // Expand the span to include the semicolon. @@ -571,9 +579,7 @@ impl<'a> Parser<'a> { Err(e) => { e.delay_as_bug(); } - _ => {} } - err.span_label(sp, "expected `{`"); err } @@ -773,17 +779,12 @@ impl<'a> Parser<'a> { let guar = err.emit(); self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); - Some(self.mk_stmt_err(self.token.span, guar)) + self.mk_stmt_err(self.token.span, guar) } Ok(stmt) => stmt, Err(err) => return Err(err), }; - if let Some(stmt) = stmt { - stmts.push(stmt); - } else { - // Found only `;` or `}`. - continue; - }; + stmts.push(stmt); } Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span))) } @@ -943,10 +944,7 @@ impl<'a> Parser<'a> { } /// Parses a statement, including the trailing semicolon. - pub fn parse_full_stmt( - &mut self, - recover: AttemptLocalParseRecovery, - ) -> PResult<'a, Option> { + pub fn parse_full_stmt(&mut self, recover: AttemptLocalParseRecovery) -> PResult<'a, Stmt> { // Skip looking for a trailing semicolon when we have a metavar seq. if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| { // Why pass `true` for `force_full_expr`? Statement expressions are less expressive @@ -959,14 +957,10 @@ impl<'a> Parser<'a> { // will reparse successfully. this.parse_stmt_without_recovery(false, ForceCollect::No, true) }) { - let stmt = stmt.expect("an actual statement"); - return Ok(Some(stmt)); + return Ok(stmt); } - let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No, false)? - else { - return Ok(None); - }; + let mut stmt = self.parse_stmt_without_recovery(true, ForceCollect::No, false)?; let mut eat_semi = true; let mut add_semi_to_stmt = false; @@ -1086,7 +1080,7 @@ impl<'a> Parser<'a> { StmtKind::Expr(_) | StmtKind::MacCall(_) => {} StmtKind::Let(local) => { if self.try_recover_let_missing_semi(local).is_some() { - return Ok(Some(stmt)); + return Ok(stmt); } if let Err(mut e) = self.expect_semi() { // We might be at the `,` in `let x = foo;`. Try to recover. @@ -1159,7 +1153,7 @@ impl<'a> Parser<'a> { } stmt.span = stmt.span.to(self.prev_token.span); - Ok(Some(stmt)) + Ok(stmt) } pub(super) fn mk_block( diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f9b508476689e..6115811b18af6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -233,6 +233,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::OnTypeError { directive, .. } => { self.check_diagnostic_on_type_error(hir_id, directive.as_deref()) } + AttributeKind::Linkage(_linkage, span) => { + self.check_linkage(*span, hir_id, target, item) + } // All of the following attributes have no specific checks. // tidy-alphabetical-start @@ -268,7 +271,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::LinkName { .. } => (), AttributeKind::LinkOrdinal { .. } => (), AttributeKind::LinkSection { .. } => (), - AttributeKind::Linkage(..) => (), AttributeKind::LoopMatch(..) => {} AttributeKind::MacroEscape => (), AttributeKind::MacroUse { .. } => (), @@ -473,7 +475,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } fn check_eii_impl(&self, impls: &[EiiImpl], target: Target) { - for EiiImpl { span, inner_span, resolution, impl_marked_unsafe, is_default: _ } in impls { + for EiiImpl { span, inner_span, resolution, impl_unsafe_span, is_default: _ } in impls { match target { Target::Fn | Target::Static => {} _ => { @@ -481,35 +483,48 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - let needs_unsafe = match resolution { - EiiImplResolution::Macro(eii_macro) => { - find_attr!(self.tcx, *eii_macro, EiiDeclaration(EiiDecl { impl_unsafe, .. }) if *impl_unsafe) - } - EiiImplResolution::Known(foreign_item_did) => { - let foreign_item_did = *foreign_item_did; - self.tcx - .externally_implementable_items(foreign_item_did.krate) - .get(&foreign_item_did) - .map(|(decl, _)| decl.impl_unsafe) - .unwrap_or(false) - } - EiiImplResolution::Error(_) => false, + let impl_unsafe = match resolution { + EiiImplResolution::Macro(eii_macro) => find_attr!( + self.tcx, + *eii_macro, + EiiDeclaration(EiiDecl { impl_unsafe, .. }) => *impl_unsafe + ), + EiiImplResolution::Known(foreign_item_did) => self + .tcx + .externally_implementable_items(foreign_item_did.krate) + .get(foreign_item_did) + .map(|(decl, _)| decl.impl_unsafe), + EiiImplResolution::Error(_) => None, + }; + let Some(needs_unsafe) = impl_unsafe else { + continue; }; - if needs_unsafe && !impl_marked_unsafe { - let name = match resolution { - EiiImplResolution::Macro(eii_macro) => self.tcx.item_name(*eii_macro), - EiiImplResolution::Known(def_id) => self.tcx.item_name(*def_id), - EiiImplResolution::Error(_) => unreachable!(), - }; - self.dcx().emit_err(diagnostics::EiiImplRequiresUnsafe { - span: *span, - name, - suggestion: diagnostics::EiiImplRequiresUnsafeSuggestion { - left: inner_span.shrink_to_lo(), - right: inner_span.shrink_to_hi(), - }, - }); + let name = match resolution { + EiiImplResolution::Macro(eii_macro) => self.tcx.item_name(*eii_macro), + EiiImplResolution::Known(def_id) => self.tcx.item_name(*def_id), + EiiImplResolution::Error(_) => unreachable!(), + }; + + match (needs_unsafe, *impl_unsafe_span) { + (true, None) => { + self.dcx().emit_err(diagnostics::EiiImplRequiresUnsafe { + span: *span, + name, + suggestion: diagnostics::EiiImplRequiresUnsafeSuggestion { + left: inner_span.shrink_to_lo(), + right: inner_span.shrink_to_hi(), + }, + }); + } + (false, Some(unsafe_span)) => { + self.dcx().emit_err(diagnostics::EiiImplCannotBeUnsafe { + impl_span: *span, + unsafe_span, + name, + }); + } + _ => {} } } } @@ -1627,6 +1642,31 @@ impl<'tcx> CheckAttrVisitor<'tcx> { .emit_err(diagnostics::BothOptimizeNoneAndInline { optimize_span, inline_span }); } } + + fn check_linkage( + &self, + span: Span, + hir_id: HirId, + target: Target, + item: Option<&'tcx Item<'tcx>>, + ) { + // FIXME(eii) Once eii is no longer so experimental, suggest doing + // linkage stuff with externally implementable items instead + match target { + Target::ForeignStatic + if self.tcx.is_mutable_static(hir_id.expect_owner().def_id.into()) => + { + self.tcx.dcx().emit_err(diagnostics::StaticMutLinkage { span }); + } + Target::Fn + if let Item { kind: ItemKind::Fn { sig, .. }, .. } = item.unwrap() + && matches!(sig.header.constness, Constness::Const { .. }) => + { + self.tcx.dcx().emit_err(diagnostics::ConstFnLinkage { span }); + } + _ => {} + } + } } impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index 34fa0b264319d..cdb14737ad90d 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -515,10 +515,10 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NoMainErr { if self.add_teach_note { diag.note(msg!("if you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/")); } + diag } } - pub(crate) struct DuplicateLangItem { pub local_span: Option, pub lang_item_name: Symbol, @@ -1082,6 +1082,16 @@ pub(crate) struct EiiImplRequiresUnsafeSuggestion { pub right: Span, } +#[derive(Diagnostic)] +#[diag("`{$name}` is not unsafe to implement")] +pub(crate) struct EiiImplCannotBeUnsafe { + #[primary_span] + pub impl_span: Span, + #[label("`unsafe` is not allowed here")] + pub unsafe_span: Span, + pub name: Symbol, +} + #[derive(Diagnostic)] #[diag("`#[{$name}]` {$kind} required, but not found")] pub(crate) struct EiiWithoutImpl { @@ -1190,3 +1200,23 @@ pub(crate) struct OnTypeErrorMalformedFormatLiterals { pub(crate) struct OnTypeErrorNotExactlyOneGeneric { pub count: usize, } + +#[derive(Diagnostic)] +#[diag("extern mutable statics are incompatible with the `linkage` attribute")] +#[note( + "the `linkage` attribute on extern statics generates a symbol that contains the address of \ + another static. Making the extern static mutable would allow changing the address, rather \ + than the static the address is pointing to" +)] +pub(crate) struct StaticMutLinkage { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag("`const fn` are incompatible with the `linkage` attribute")] +#[note("`const fn` may be called at compile time, which happens before linking")] +pub(crate) struct ConstFnLinkage { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 318d0b60e240d..b8cf472309ba5 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -2126,10 +2126,8 @@ impl fmt::Debug for SourceFile { /// /// When `SourceFile`s are exported in crate metadata, the `StableSourceFileId` /// is updated to incorporate the `StableCrateId` of the exporting crate. -#[derive( - Debug, Clone, Copy, Hash, PartialEq, Eq, StableHash, Encodable, Decodable, Default, PartialOrd, - Ord -)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Default, Ord)] +#[derive(StableHash, Encodable, Decodable)] pub struct StableSourceFileId(Hash128); impl StableSourceFileId { diff --git a/compiler/rustc_target/src/asm/amdgpu.rs b/compiler/rustc_target/src/asm/amdgpu.rs index cc6f612cdb8b5..0f24ae5dea225 100644 --- a/compiler/rustc_target/src/asm/amdgpu.rs +++ b/compiler/rustc_target/src/asm/amdgpu.rs @@ -1,5 +1,6 @@ use std::fmt; +use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_span::Symbol; use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; @@ -9,19 +10,8 @@ use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; /// Amdgpu register classes /// /// The number is the size of the register class in bits. -#[derive( - Copy, - Clone, - rustc_macros::Encodable, - rustc_macros::Decodable, - Debug, - Eq, - PartialEq, - PartialOrd, - Hash, - rustc_macros::StableHash -)] -#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[derive(StableHash, Encodable, Decodable)] pub enum AmdgpuInlineAsmRegClass { Sgpr(u16), Vgpr(u16), @@ -267,18 +257,8 @@ impl AmdgpuInlineAsmRegClass { /// Start index of a register. /// /// Together with the register size this gives the range occupied by a register. -#[derive( - Copy, - Clone, - rustc_macros::Encodable, - rustc_macros::Decodable, - Debug, - Eq, - PartialEq, - PartialOrd, - Hash, - rustc_macros::StableHash -)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[derive(Encodable, Decodable, StableHash)] enum AmdgpuRegStart { /// Low 16-bit of the register at this index Low(u16), @@ -288,19 +268,8 @@ enum AmdgpuRegStart { Full(u16), } -#[derive( - Copy, - Clone, - rustc_macros::Encodable, - rustc_macros::Decodable, - Debug, - Eq, - PartialEq, - PartialOrd, - Hash, - rustc_macros::StableHash -)] -#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[derive(Encodable, Decodable, StableHash)] pub struct AmdgpuInlineAsmReg { class: AmdgpuInlineAsmRegClass, range: AmdgpuRegStart, diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 4ec21b9f8a4cd..8179b0f6f01a1 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -951,7 +951,35 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { } match project::poly_project_and_unify_term(&mut self.selcx, &project_obligation) { - ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(obligation, os)), + ProjectAndUnifyResult::Holds(os) if os.is_empty() => { + ProcessResult::Changed(mk_pending(obligation, os)) + } + ProjectAndUnifyResult::Holds(os) => { + let input_projection_term = infcx + .resolve_vars_if_possible(project_obligation.predicate) + .map_bound(|p| p.projection_term); + let all_same_projection_term = os.iter().all(|o| { + let Some(proj_clause) = o.predicate.as_projection_clause() else { + return false; + }; + infcx.resolve_vars_if_possible(proj_clause).map_bound(|p| p.projection_term) + == input_projection_term + }); + if all_same_projection_term { + // Every nested obligation has the same projection term as the obligation + // we are processing, so registering would make fulfillment process the same + // obligation forever. This happens when unifying the projection with the + // predicate's term spawns a delayed copy of the predicate itself, see + // `InferCtxt::instantiate_var`. E.g. in Issue #159750, processing + // `<_ as Queryable>::Output == ?0` returns `Holds` with the single nested + // obligation `<_ as Queryable>::Output == ?1` where `?1` is merely unioned + // with `?0`. + // Since at this point the code will not compile, error immediately. + ProcessResult::Error(FulfillmentErrorCode::Ambiguity { overflow: None }) + } else { + ProcessResult::Changed(mk_pending(obligation, os)) + } + } ProjectAndUnifyResult::FailedNormalization => { stalled_on.clear(); stalled_on.extend(args_infer_vars( diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 58996703023ce..5190baaab8a26 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1368,7 +1368,7 @@ impl Box { /// /// unsafe { /// let non_null = NonNull::new(alloc(Layout::new::()).cast::()) - /// .expect("allocation failed"); + /// .expect("alloc should have successfully allocated memory"); /// // In general .write is required to avoid attempting to destruct /// // the (uninitialized) previous contents of `non_null`. /// non_null.write(5); diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 34de563fe4f33..98192e1fb5b01 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -1258,7 +1258,7 @@ impl BinaryHeap { /// /// Ok(heap.pop()) /// } - /// # find_max_slow(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// # find_max_slow(&[1, 2, 3]).expect("reserving capacity for 12 bytes should never fail"); /// ``` #[stable(feature = "try_reserve_2", since = "1.63.0")] pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { @@ -1294,7 +1294,7 @@ impl BinaryHeap { /// /// Ok(heap.pop()) /// } - /// # find_max_slow(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// # find_max_slow(&[1, 2, 3]).expect("reserving capacity for 12 bytes should never fail"); /// ``` #[stable(feature = "try_reserve_2", since = "1.63.0")] pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index e6e6fcf5420f3..b340cf9566f2e 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -84,7 +84,7 @@ use crate::vec::Vec; /// /// // We are certain that our string doesn't have 0 bytes in the middle, /// // so we can .expect() -/// let c_to_print = CString::new("Hello, world!").expect("CString::new failed"); +/// let c_to_print = CString::new("Hello, world!").expect("we provided a string without NUL bytes, so CString::new should not fail"); /// unsafe { /// my_printer(c_to_print.as_ptr()); /// } @@ -242,7 +242,7 @@ impl CString { /// /// extern "C" { fn puts(s: *const c_char); } /// - /// let to_print = CString::new("Hello!").expect("CString::new failed"); + /// let to_print = CString::new("Hello!").expect("we provided a string without NUL bytes, so CString::new should not fail"); /// unsafe { /// puts(to_print.as_ptr()); /// } @@ -466,12 +466,12 @@ impl CString { /// use std::ffi::CString; /// /// let valid_utf8 = vec![b'f', b'o', b'o']; - /// let cstring = CString::new(valid_utf8).expect("CString::new failed"); - /// assert_eq!(cstring.into_string().expect("into_string() call failed"), "foo"); + /// let cstring = CString::new(valid_utf8).expect("we provided bytes that do not have a NUL byte, so CString::new should not fail"); + /// assert_eq!(cstring.into_string().expect("we provided bytes that are valid UTF-8, so `into_string` should not fail"), "foo"); /// /// let invalid_utf8 = vec![b'f', 0xff, b'o', b'o']; - /// let cstring = CString::new(invalid_utf8).expect("CString::new failed"); - /// let err = cstring.into_string().err().expect("into_string().err() failed"); + /// let cstring = CString::new(invalid_utf8).expect("we provided bytes that do not have a NUL byte, so CString::new should not fail"); + /// let err = cstring.into_string().expect_err("we provided bytes that are invalid UTF-8, so `into_string` should fail"); /// assert_eq!(err.utf8_error().valid_up_to(), 1); /// ``` #[stable(feature = "cstring_into", since = "1.7.0")] @@ -577,7 +577,7 @@ impl CString { /// let c_string = CString::from(c"foo"); /// let cstr = c_string.as_c_str(); /// assert_eq!(cstr, - /// CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed")); + /// CStr::from_bytes_with_nul(b"foo\0").expect("we provided bytes that has one NUL byte exactly at the end, so CStr::from_bytes_with_nul should not fail")); /// ``` #[inline] #[must_use] @@ -660,7 +660,7 @@ impl CString { /// use std::ffi::CString; /// assert_eq!( /// CString::from_vec_with_nul(b"abc\0".to_vec()) - /// .expect("CString::from_vec_with_nul failed"), + /// .expect("we provided bytes that has one NUL byte exactly at the end, so CString::from_vec_with_nul should not fail"), /// c"abc".to_owned() /// ); /// ``` diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 6c19ba816050d..cc321660e6ea4 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1314,7 +1314,7 @@ impl String { /// /// Ok(output) /// } - /// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?"); + /// # process_data("rust").expect("reserving capacity for 12 bytes should never fail"); /// ``` #[stable(feature = "try_reserve", since = "1.57.0")] pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { @@ -1355,7 +1355,7 @@ impl String { /// /// Ok(output) /// } - /// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?"); + /// # process_data("rust").expect("reserving capacity for 12 bytes should never fail"); /// ``` #[stable(feature = "try_reserve", since = "1.57.0")] pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 78bd6a93e7df3..46b56cabf2873 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3302,8 +3302,9 @@ impl Vec { /// assert_eq!(&v, &[0, 1, 2]); /// ``` #[stable(feature = "vec_spare_capacity", since = "1.60.0")] + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[inline] - pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { + pub const fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { // Note: // This method is not implemented in terms of `split_at_spare_mut`, // to prevent invalidation of pointers to the buffer. @@ -3367,8 +3368,9 @@ impl Vec { /// assert_eq!(&v, &[1, 1, 2, 4, 8, 12, 16]); /// ``` #[unstable(feature = "vec_split_at_spare", issue = "81944")] + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[inline] - pub fn split_at_spare_mut(&mut self) -> (&mut [T], &mut [MaybeUninit]) { + pub const fn split_at_spare_mut(&mut self) -> (&mut [T], &mut [MaybeUninit]) { // SAFETY: // - len is ignored and so never changed let (init, spare, _) = unsafe { self.split_at_spare_mut_with_len() }; @@ -3378,7 +3380,7 @@ impl Vec { /// Safety: changing returned .2 (&mut usize) is considered the same as calling `.set_len(_)`. /// /// This method provides unique access to all vec parts at once in `extend_from_within`. - unsafe fn split_at_spare_mut_with_len( + const unsafe fn split_at_spare_mut_with_len( &mut self, ) -> (&mut [T], &mut [MaybeUninit], &mut usize) { let ptr = self.as_mut_ptr(); diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 5e80ea2a78843..7ccc8d9c6a115 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -33,6 +33,7 @@ #![feature(slice_ptr_get)] #![feature(slice_range)] #![feature(str_as_str)] +#![feature(str_copy_from_str)] #![feature(strict_provenance_lints)] #![feature(string_remove_matches)] #![feature(string_replace_in_place)] diff --git a/library/alloctests/tests/str.rs b/library/alloctests/tests/str.rs index 9f39f578935ad..830f6972f5af5 100644 --- a/library/alloctests/tests/str.rs +++ b/library/alloctests/tests/str.rs @@ -1108,6 +1108,21 @@ fn test_split_at_boundscheck() { let _ = s.split_at(1); } +#[test] +fn test_copy_from_str() { + let src = "Saludos"; + let mut dst = "Grüße, Jürgen".to_string(); + dst[..7].copy_from_str(src); + assert_eq!(dst, "Saludos, Jürgen"); +} + +#[test] +#[should_panic] +fn test_copy_from_str_unequal_len() { + let mut dst = "hello".to_string(); + dst.copy_from_str("hi"); +} + #[test] fn test_escape_unicode() { assert_eq!("abc".escape_unicode().to_string(), "\\u{61}\\u{62}\\u{63}"); diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 939d3df26ba24..4332e7d28c58b 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -116,7 +116,7 @@ pub macro unreachable_2021 { /// convert unwinds to aborts, so using this function isn't necessary for FFI. #[unstable(feature = "abort_unwind", issue = "130338")] #[rustc_nounwind] -pub fn abort_unwind R, R>(f: F) -> R { +pub fn abort_on_unwind R, R>(f: F) -> R { f() } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 4aea78ccd94cb..67e8f5c032b7b 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -259,7 +259,7 @@ impl NonNull { /// use std::ptr::NonNull; /// /// let mut x = 0u32; - /// let ptr = NonNull::::new(&mut x as *mut _).expect("ptr is null!"); + /// let ptr = NonNull::::new(&mut x as *mut _).expect("pointer should not be null"); /// /// if let Some(ptr) = NonNull::::new(std::ptr::null_mut()) { /// unreachable!(); @@ -386,7 +386,7 @@ impl NonNull { /// use std::ptr::NonNull; /// /// let mut x = 0u32; - /// let ptr = NonNull::new(&mut x).expect("ptr is null!"); + /// let ptr = NonNull::new(&mut x).expect("pointer should not be null"); /// /// let x_value = unsafe { *ptr.as_ptr() }; /// assert_eq!(x_value, 0); @@ -428,7 +428,7 @@ impl NonNull { /// use std::ptr::NonNull; /// /// let mut x = 0u32; - /// let ptr = NonNull::new(&mut x as *mut _).expect("ptr is null!"); + /// let ptr = NonNull::new(&mut x as *mut _).expect("pointer should not be null"); /// /// let ref_x = unsafe { ptr.as_ref() }; /// println!("{ref_x}"); @@ -464,7 +464,7 @@ impl NonNull { /// use std::ptr::NonNull; /// /// let mut x = 0u32; - /// let mut ptr = NonNull::new(&mut x).expect("null pointer"); + /// let mut ptr = NonNull::new(&mut x).expect("pointer should not be null"); /// /// let x_ref = unsafe { ptr.as_mut() }; /// assert_eq!(*x_ref, 0); @@ -491,7 +491,7 @@ impl NonNull { /// use std::ptr::NonNull; /// /// let mut x = 0u32; - /// let ptr = NonNull::new(&mut x as *mut _).expect("null pointer"); + /// let ptr = NonNull::new(&mut x as *mut _).expect("pointer should not be null"); /// /// let casted_ptr = ptr.cast::(); /// let raw_ptr: *mut i8 = casted_ptr.as_ptr(); diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index c465e3cb0a230..206b32401ceef 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2969,6 +2969,69 @@ impl str { me.make_ascii_lowercase() } + /// Copies the string from `src` into `self`, using a memcpy. + /// + /// The length of `src` must be the same as `self`. + /// + /// # Panics + /// + /// This function will panic if the two strings have different lengths. + /// + /// # Examples + /// + /// ``` + /// #![feature(str_copy_from_str)] + /// let src = "Saludos"; + /// let mut dst = String::from("Grüße, Jürgen"); + /// + /// // Because the strings have to be the same length, + /// // we slice the destination slice from sixteen bytes + /// // to seven. It will panic if we don't do this. + /// dst[..7].copy_from_str(src); + /// + /// assert_eq!(src, "Saludos"); + /// assert_eq!(dst, "Saludos, Jürgen"); + /// ``` + /// + /// Rust enforces that there can only be one mutable reference with no + /// immutable references to a particular piece of data in a particular + /// scope. Because of this, attempting to use `copy_from_str` on a + /// single string will result in a compile failure: + /// + /// ```compile_fail + /// #![feature(str_copy_from_str)] + /// let mut string = String::from("Abcde"); + /// + /// string[..2].copy_from_str(&string[3..]); // compile fail! + /// ``` + /// + /// To work around this, we can use [`split_at_mut`] to create two distinct + /// sub-slices from a string: + /// + /// ``` + /// #![feature(str_copy_from_str)] + /// let mut string = String::from("Abcde"); + /// + /// { + /// let (left, right) = string.split_at_mut(2); + /// left.copy_from_str(&right[1..]); + /// } + /// + /// assert_eq!(string, "decde"); + /// ``` + /// + /// [`split_at_mut`]: str::split_at_mut + #[doc(alias = "memcpy")] + #[inline] + #[unstable(feature = "str_copy_from_str", issue = "159841")] + #[track_caller] + pub fn copy_from_str(&mut self, src: &str) { + // SAFETY: `copy_from_slice` panics unless the lengths are equal, and copying same-length + // UTF-8 into a `str` keeps it valid UTF-8. + let me = unsafe { self.as_bytes_mut() }; + me.copy_from_slice(src.as_bytes()); + } + /// Returns a string slice with leading ASCII whitespace removed. /// /// 'Whitespace' refers to the definition used by diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 0622637e72738..774888dfda109 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -3432,23 +3432,65 @@ pub fn set_permissions>(path: P, perm: Permissions) -> io::Result fs_imp::set_permissions(path.as_ref(), perm.0) } -/// Set the permissions of a file, unless it is a symlink. +/// Changes the permissions found on a file or a directory. On certain platforms, if the file +/// is a symlink, it will change the permissions bits on the symlink itself rather than +/// the target (e.g. Windows, BSD, MacOS). On other platforms, this results in an error when +/// attempting to change permissions on a symlink (e.g. Linux). /// -/// Note that the non-final path elements are allowed to be symlinks. +/// Note that non-final path elements are allowed to be symlinks. /// /// # Platform-specific behavior /// -/// Currently unimplemented on Windows. +/// This function currently corresponds to: +/// * `open` with `O_NOFOLLOW` flag enabled + `fchmod` on WASI +/// * `fchmodat` function with the flag `AT_SYMLINK_NOFOLLOW` enabled +/// on Unix platforms +/// * The flag `FILE_FLAG_OPEN_REPARSE_POINT` is enabled and then the +/// permissions of the file is set through `SetFileInformationByHandle` +/// on Windows. +/// * On all other platforms, the behavior remains the same with +/// [`fs::set_permissions`]. /// -/// On Unix platforms, this results in a [`FilesystemLoop`] error if the last element is a symlink. +/// [`fs::set_permissions`]: crate::fs::set_permissions /// -/// This behavior may change in the future. +/// Note that, this [may change in the future][changes]. /// -/// [`FilesystemLoop`]: crate::io::ErrorKind::FilesystemLoop -#[doc(alias = "chmod", alias = "SetFileAttributes")] +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` does not exist. +/// * The user lacks the permission to change attributes of the file. +/// +/// Note: On Linux, this will result in a [`Unsupported`] error +/// if the final element is a symlink. On BSD-based systems, the +/// behavior can vary from symlink permission bits changing or +/// there being no effects on symlinks +/// +/// [`Unsupported`]: crate::io::ErrorKind::Unsupported +/// +/// # Examples +/// +/// ```no_run +/// #![feature(set_permissions_nofollow)] +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// let mut perms = fs::symlink_metadata("foo.txt")?.permissions(); +/// perms.set_readonly(true); +/// // This should result in an error on certain platforms +/// // or succeed in modifying the permissions of a symlink +/// fs::set_permissions_nofollow("foo.txt", perms)?; +/// Ok(()) +/// } +/// ``` +#[doc(alias = "fchmodat", alias = "SetFileInformationByHandle")] #[unstable(feature = "set_permissions_nofollow", issue = "141607")] pub fn set_permissions_nofollow>(path: P, perm: Permissions) -> io::Result<()> { - fs_imp::set_permissions_nofollow(path.as_ref(), perm) + fs_imp::set_permissions_nofollow(path.as_ref(), perm.0) } impl DirBuilder { diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 4f2fa7fbc591e..3ecf27562b9dc 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -613,6 +613,70 @@ fn set_get_unix_permissions() { assert_eq!(mask & metadata1.permissions().mode(), 0o0777); } +#[test] +fn set_get_permissions_nofollows() { + let tmpdir = tmpdir(); + let filename = tmpdir.join("set_get_unix_permissions_file"); + check!(File::create(&filename)); + let file_metadata = check!(fs::metadata(&filename)); + assert!(!file_metadata.permissions().readonly()); + let mut permission_bits = file_metadata.permissions(); + permission_bits.set_readonly(true); + let result = fs::set_permissions_nofollow(&filename, permission_bits); + + cfg_select! { + any(windows, unix, target_os = "uefi", target_os = "solid_asp3", target_os = "motor") => { + assert_eq!(result.unwrap(), ()); + let metadata0 = check!(fs::metadata(&filename)); + assert!(metadata0.permissions().readonly()); + }, + _ => { + let error_kind = result.unwrap_err().kind(); + assert_eq!(error_kind, crate::io::ErrorKind::Unsupported); + } + } +} + +// Only Windows and Unix support `fs::set_permissions_nofollow` +#[test] +#[cfg(all(any(windows, unix), not(any(target_os = "espidf", target_os = "horizon"))))] +fn set_get_permissions_nofollows_symlink() { + #[cfg(not(windows))] + use crate::os::unix::fs::symlink; + #[cfg(windows)] + use crate::os::windows::fs::symlink_dir; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("set_get_unix_permissions_file"); + let symlink_name = tmpdir.join("set_get_unix_permissions"); + check!(File::create(&filename)); + #[cfg(not(windows))] + check!(symlink(&filename, &symlink_name)); + #[cfg(windows)] + check!(symlink_dir(&filename, &symlink_name)); + + let sym_metadata = check!(fs::symlink_metadata(&symlink_name)); + let mut permission_bits = sym_metadata.permissions(); + permission_bits.set_readonly(true); + let result = fs::set_permissions_nofollow(&symlink_name, permission_bits); + + cfg_select! { + any(target_os = "android", target_os = "macos", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly") => { + assert_eq!(result.unwrap(), ()); + let metadata0 = check!(fs::symlink_metadata(&symlink_name)); + // So seems like BSD-based systems trying to set permissions + // on symlinks could lead to no effect, so we should expect + // there being no change to BSD-based systems. + // https://superuser.com/questions/1099634/change-permissions-symbolic-link-mac-os + assert!(!metadata0.permissions().readonly()); + }, + _ => { + let error_kind = result.unwrap_err().kind(); + assert_eq!(error_kind, crate::io::ErrorKind::Unsupported); + } + } +} + #[test] #[cfg(windows)] fn file_test_io_seek_read_write() { @@ -1330,6 +1394,29 @@ fn fchmod_works() { check!(file.set_permissions(p)); } +#[test] +fn fchmodat_works() { + let tmpdir = tmpdir(); + let file = tmpdir.join("in.txt"); + + check!(File::create(&file)); + let attr = check!(fs::metadata(&file)); + assert!(!attr.permissions().readonly()); + let mut p = attr.permissions(); + p.set_readonly(true); + check!(fs::set_permissions_nofollow(&file, p.clone())); + let attr = check!(fs::metadata(&file)); + assert!(attr.permissions().readonly()); + + match fs::set_permissions_nofollow(&tmpdir.join("foo"), p.clone()) { + Ok(..) => panic!("wanted an error"), + Err(..) => {} + } + + p.set_readonly(false); + check!(fs::set_permissions_nofollow(&file, p)); +} + #[test] fn sync_doesnt_kill_anything() { let tmpdir = tmpdir(); diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index e8394feb73f5d..a07a5fb6290ca 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -285,7 +285,7 @@ where } #[unstable(feature = "abort_unwind", issue = "130338")] -pub use core::panic::abort_unwind; +pub use core::panic::abort_on_unwind; /// Invokes a closure, capturing the cause of an unwinding panic if one occurs. /// diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 1e7de695ddae7..35d17d56ddd49 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -166,8 +166,8 @@ fn lang_start_internal( // prevent std from accidentally introducing a panic to these functions. Another is from // user code from `main` or, more nefariously, as described in e.g. issue #86030. // - // We use `catch_unwind` with `handle_rt_panic` instead of `abort_unwind` to make the error in - // case of a panic a bit nicer. + // We use `catch_unwind` with `handle_rt_panic` instead of `abort_on_unwind` to make the error + // in case of a panic a bit nicer. panic::catch_unwind(move || { // SAFETY: Only called once during runtime initialization. unsafe { init(argc, argv, sigpipe) }; diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs index 5f69560998293..d29ea81c67e6d 100644 --- a/library/std/src/sys/fs/hermit.rs +++ b/library/std/src/sys/fs/hermit.rs @@ -566,6 +566,10 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { Err(Error::from_raw_os_error(22)) } +pub fn set_perm_nofollow(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + unsupported() +} + pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { Err(Error::from_raw_os_error(22)) } diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index 0c297c5766b82..b5a5fa93fd491 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -120,28 +120,8 @@ pub fn set_permissions(path: &Path, perm: FilePermissions) -> io::Result<()> { with_native_path(path, &|path| imp::set_perm(path, perm.clone())) } -#[cfg(all(unix, not(target_os = "vxworks")))] -pub fn set_permissions_nofollow(path: &Path, perm: crate::fs::Permissions) -> io::Result<()> { - use crate::fs::OpenOptions; - - let mut options = OpenOptions::new(); - - // ESP-IDF and Horizon do not support O_NOFOLLOW, so we skip setting it. - // Their filesystems do not have symbolic links, so no special handling is required. - #[cfg(not(any(target_os = "espidf", target_os = "horizon")))] - { - use crate::os::unix::fs::OpenOptionsExt; - options.custom_flags(libc::O_NOFOLLOW); - } - - options.open(path)?.set_permissions(perm) -} - -#[cfg(any(not(unix), target_os = "vxworks"))] -pub fn set_permissions_nofollow(_path: &Path, _perm: crate::fs::Permissions) -> io::Result<()> { - crate::unimplemented!( - "`set_permissions_nofollow` is currently only implemented on Unix platforms" - ) +pub fn set_permissions_nofollow(path: &Path, perm: FilePermissions) -> io::Result<()> { + with_native_path(path, &|path| imp::set_perm_nofollow(path, perm.clone())) } pub fn canonicalize(path: &Path) -> io::Result { diff --git a/library/std/src/sys/fs/motor.rs b/library/std/src/sys/fs/motor.rs index 51b7f347b0b17..a76f64a47c24a 100644 --- a/library/std/src/sys/fs/motor.rs +++ b/library/std/src/sys/fs/motor.rs @@ -319,6 +319,11 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> { } pub fn set_perm(path: &Path, perm: FilePermissions) -> io::Result<()> { + // Motor does not support symlinks + set_perm_nofollow(path, perm) +} + +pub fn set_perm_nofollow(path: &Path, perm: FilePermissions) -> io::Result<()> { let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; moto_rt::fs::set_perm(path, perm.rt_perm).map_err(map_motor_error) } diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index b61147db57ed5..bd963b1d2f038 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -531,6 +531,11 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> { } pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + // Solid does not support symlinks + set_perm_nofollow(p, perm) +} + +pub fn set_perm_nofollow(p: &Path, perm: FilePermissions) -> io::Result<()> { error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Chmod(cstr(p)?.as_ptr(), perm.0.into()) }) diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index c4467ed23e125..1a0da329ce1a3 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -480,6 +480,11 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> { } pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + // UEFI does not support symlinks + set_perm_nofollow(p, perm) +} + +pub fn set_perm_nofollow(p: &Path, perm: FilePermissions) -> io::Result<()> { let f = uefi_fs::File::from_path(p, file::MODE_READ | file::MODE_WRITE, 0)?; set_perm_inner(&f, perm) } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 875b808e14b4b..147234f345f45 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -2035,6 +2035,40 @@ pub fn set_perm(p: &CStr, perm: FilePermissions) -> io::Result<()> { cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ()) } +pub fn set_perm_nofollow(p: &CStr, perm: FilePermissions) -> io::Result<()> { + // ESP-IDF and Horizon do not support O_NOFOLLOW, so we skip setting it. + // Their filesystems do not have symbolic links, so no special handling is required. + cfg_select! { + // wasm32-wasip1 targets do not support fchmodat, so we fall down to + // open + fchmod + target_os = "wasi" => { + use crate::fs::OpenOptions; + use crate::fs::Permissions; + use crate::os::wasi::ffi::OsStrExt; + use crate::os::wasi::fs::OpenOptionsExt; + + let mut options = OpenOptions::new(); + options.custom_flags(libc::O_NOFOLLOW); + + let bytes = p.to_bytes(); + let os_str = OsStr::from_bytes(bytes); + options.open(Path::new(os_str))?.set_permissions(Permissions::from_inner(perm)) + } + all(target_os = "linux", not(any(target_os = "espidf", target_os = "horizon"))) => { + cvt_r(|| unsafe { + libc::fchmodat(libc::AT_FDCWD, p.as_ptr(), perm.mode, libc::AT_SYMLINK_NOFOLLOW) + }) + .map(|_| ()) + }, + _ => { + cvt_r(|| unsafe { + libc::fchmodat(libc::AT_FDCWD, p.as_ptr(), perm.mode, 0) + }) + .map(|_| ()) + } + } +} + pub fn rmdir(p: &CStr) -> io::Result<()> { cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ()) } diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 703ebef380383..512af27401dfe 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -313,6 +313,10 @@ pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { match perm.0 {} } +pub fn set_perm_nofollow(_p: &Path, perm: FilePermissions) -> io::Result<()> { + match perm.0 {} +} + pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs index 595296bd85c8a..1357787c44c77 100644 --- a/library/std/src/sys/fs/vexos.rs +++ b/library/std/src/sys/fs/vexos.rs @@ -492,6 +492,10 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { unsupported() } +pub fn set_perm_nofollow(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + unsupported() +} + pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index e3e7b081b47d5..a7e8e8b3ac630 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -1564,6 +1564,15 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> { } } +pub fn set_perm_nofollow(p: &WCStr, perm: FilePermissions) -> io::Result<()> { + let mut opts = OpenOptions::new(); + opts.access_mode(c::FILE_WRITE_ATTRIBUTES); + // `FILE_FLAG_OPEN_REPARSE_POINT` for no_follow behavior + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); + let file = File::open_native(p, &opts)?; + file.set_permissions(perm) +} + pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> { let mut opts = OpenOptions::new(); opts.access_mode(c::FILE_WRITE_ATTRIBUTES); diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index d48bb1c8b721e..cb954e475be1b 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -25,7 +25,7 @@ cfg_select! { any( - all(target_family = "wasm", not(target_feature = "atomics")), + all(target_family = "wasm", not(target_feature = "atomics"), not(target_os = "wasi")), target_os = "uefi", target_os = "zkvm", target_os = "trusty", @@ -54,7 +54,10 @@ cfg_select! { /// destructor for each variable. On these platforms, we keep track of the /// destructors ourselves and register (through the [`guard`] module) only a /// single callback that runs all of the destructors in the list. -#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))] +#[cfg(all( + target_thread_local, + not(all(target_family = "wasm", not(target_feature = "atomics"), not(target_os = "wasi"))) +))] pub(crate) mod destructors { cfg_select! { any( @@ -93,9 +96,7 @@ pub(crate) mod guard { pub(crate) use windows::enable; } any( - all(target_family = "wasm", not( - all(target_os = "wasi", target_env = "p1", target_feature = "atomics") - )), + all(target_family = "wasm", not(target_os = "wasi")), target_os = "uefi", target_os = "zkvm", target_os = "trusty", @@ -150,7 +151,7 @@ pub(crate) mod key { ), all(not(target_thread_local), target_vendor = "apple"), target_os = "teeos", - all(target_os = "wasi", target_env = "p1", target_feature = "atomics"), + target_os = "wasi", ) => { mod racy; mod unix; diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 002b46cb7fbec..1bb925f726a16 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -13,7 +13,8 @@ use crate::core::build_steps::tool::{ prepare_tool_cargo, }; use crate::core::builder::{ - self, Alias, Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, + self, Alias, Builder, Cargo, CommandLineStep, Kind, RunConfig, ShouldRun, Step, StepMetadata, + crate_description, }; use crate::core::config::TargetSelection; use crate::utils::build_stamp::{self, BuildStamp}; @@ -36,7 +37,7 @@ impl Std { const CRATE_OR_DEPS: &[&str] = &["sysroot", "coretests", "alloctests"]; } -impl Step for Std { +impl CommandLineStep for Std { type Output = BuildStamp; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -222,10 +223,6 @@ impl PrepareRustcRmetaSysroot { impl Step for PrepareRustcRmetaSysroot { type Output = RmetaSysroot; - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - fn run(self, builder: &Builder<'_>) -> Self::Output { // Check rustc let stamp = builder.ensure(Rustc::from_build_compiler( @@ -266,10 +263,6 @@ impl PrepareStdRmetaSysroot { impl Step for PrepareStdRmetaSysroot { type Output = RmetaSysroot; - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - fn run(self, builder: &Builder<'_>) -> Self::Output { // Check std let stamp = builder.ensure(Std { @@ -317,7 +310,7 @@ impl Rustc { } } -impl Step for Rustc { +impl CommandLineStep for Rustc { type Output = BuildStamp; const IS_HOST: bool = true; @@ -528,7 +521,7 @@ pub struct CraneliftCodegenBackend { target: TargetSelection, } -impl Step for CraneliftCodegenBackend { +impl CommandLineStep for CraneliftCodegenBackend { type Output = (); const IS_HOST: bool = true; @@ -607,7 +600,7 @@ pub struct GccCodegenBackend { target: TargetSelection, } -impl Step for GccCodegenBackend { +impl CommandLineStep for GccCodegenBackend { type Output = (); const IS_HOST: bool = true; @@ -701,7 +694,7 @@ macro_rules! tool_check_step { target: TargetSelection, } - impl Step for $name { + impl CommandLineStep for $name { type Output = (); const IS_HOST: bool = true; diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index fec4a79de3c75..292885e99a34b 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -9,7 +9,7 @@ use std::fs; use std::io::{self, ErrorKind}; use std::path::Path; -use crate::core::builder::{Builder, RunConfig, ShouldRun, Step, crate_description}; +use crate::core::builder::{Builder, CommandLineStep, RunConfig, ShouldRun, crate_description}; use crate::utils::build_stamp::BuildStamp; use crate::utils::helpers::t; use crate::{Build, Compiler, Kind, Mode, Subcommand}; @@ -17,12 +17,13 @@ use crate::{Build, Compiler, Kind, Mode, Subcommand}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CleanAll {} -impl Step for CleanAll { +impl CommandLineStep for CleanAll { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - // Only runs as the default `./x clean` step; cannot be selected explicitly. - run.never() + // Normally this step is invoked implicitly via `./x clean`, but all + // steps are required to register at least one explicit path/alias. + run.alias("default") } fn is_default_step(_builder: &Builder<'_>) -> bool { @@ -54,7 +55,7 @@ macro_rules! clean_crate_tree { crates: Vec, } - impl Step for $name { + impl CommandLineStep for $name { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 09ccd14ab88c3..8df931a24fd25 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -20,7 +20,9 @@ use crate::core::build_steps::compile::{ ArtifactKeepMode, run_cargo, rustc_cargo, std_cargo, std_crates_for_make_run, }; use crate::core::builder; -use crate::core::builder::{Alias, Kind, RunConfig, Step, StepMetadata, crate_description}; +use crate::core::builder::{ + Alias, CommandLineStep, Kind, RunConfig, StepMetadata, crate_description, +}; use crate::utils::build_stamp::{self, BuildStamp}; use crate::{Compiler, Mode, Subcommand, TargetSelection, exit}; @@ -167,7 +169,7 @@ impl Std { } } -impl Step for Std { +impl CommandLineStep for Std { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -251,7 +253,7 @@ impl Rustc { } } -impl Step for Rustc { +impl CommandLineStep for Rustc { type Output = (); const IS_HOST: bool = true; @@ -336,7 +338,7 @@ impl CodegenGcc { } } -impl Step for CodegenGcc { +impl CommandLineStep for CodegenGcc { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -422,7 +424,7 @@ macro_rules! lint_any { config: LintConfig, } - impl Step for $name { + impl CommandLineStep for $name { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -522,7 +524,7 @@ pub struct CI { config: LintConfig, } -impl Step for CI { +impl CommandLineStep for CI { type Output = (); 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 d186e15c75cf2..da8449f4f637b 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -22,9 +22,9 @@ use tracing::span; use crate::core::build_steps::gcc::{Gcc, GccOutput, GccTargetPair}; use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts}; use crate::core::build_steps::{dist, llvm}; -use crate::core::builder; use crate::core::builder::{ - Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, apply_pgo, crate_description, + self, Builder, Cargo, CommandLineStep, Kind, RunConfig, ShouldRun, Step, StepMetadata, + apply_pgo, crate_description, }; use crate::core::config::toml::target::DefaultLinuxLinkerOverride; use crate::core::config::{ @@ -109,7 +109,7 @@ impl Std { } } -impl Step for Std { +impl CommandLineStep for Std { /// Build stamp of std, if it was indeed built or uplifted. type Output = Option; @@ -743,10 +743,6 @@ impl StdLink { impl Step for StdLink { type Output = (); - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - /// Link all libstd rlibs/dylibs into the sysroot location. /// /// Links those artifacts generated by `compiler` to the `stage` compiler's @@ -900,7 +896,7 @@ pub struct StartupObjects { pub target: TargetSelection, } -impl Step for StartupObjects { +impl CommandLineStep for StartupObjects { type Output = Vec<(PathBuf, DependencyType)>; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1013,7 +1009,7 @@ impl Rustc { } } -impl Step for Rustc { +impl CommandLineStep for Rustc { type Output = BuiltRustc; const IS_HOST: bool = true; @@ -1535,10 +1531,6 @@ impl RustcLink { impl Step for RustcLink { type Output = (); - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - /// Same as `StdLink`, only for librustc fn run(self, builder: &Builder<'_>) { let build_compiler = self.build_compiler; @@ -1659,7 +1651,7 @@ impl GccCodegenBackend { } } -impl Step for GccCodegenBackend { +impl CommandLineStep for GccCodegenBackend { type Output = GccCodegenBackendOutput; const IS_HOST: bool = true; @@ -1728,7 +1720,7 @@ pub struct CraneliftCodegenBackend { pub compilers: RustcPrivateCompilers, } -impl Step for CraneliftCodegenBackend { +impl CommandLineStep for CraneliftCodegenBackend { type Output = BuildStamp; const IS_HOST: bool = true; @@ -1909,10 +1901,6 @@ impl Sysroot { impl Step for Sysroot { type Output = PathBuf; - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - /// 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. @@ -2078,7 +2066,7 @@ pub struct Assemble { pub target_compiler: Compiler, } -impl Step for Assemble { +impl CommandLineStep for Assemble { type Output = Compiler; const IS_HOST: bool = true; diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index b74dd7dc3b987..09aadee694d32 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -29,7 +29,9 @@ use crate::core::build_steps::tool::{ }; use crate::core::build_steps::vendor::Vendor; use crate::core::build_steps::{compile, llvm}; -use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata}; +use crate::core::builder::{ + Builder, CommandLineStep, Kind, RunConfig, ShouldRun, Step, StepMetadata, +}; use crate::core::config::{GccCiMode, TargetSelection}; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::channel::{self, Info}; @@ -64,7 +66,7 @@ pub struct Docs { pub host: TargetSelection, } -impl Step for Docs { +impl CommandLineStep for Docs { type Output = Option; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -116,7 +118,7 @@ pub struct JsonDocs { target: TargetSelection, } -impl Step for JsonDocs { +impl CommandLineStep for JsonDocs { type Output = Option; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -167,7 +169,7 @@ pub struct RustcDocs { target: TargetSelection, } -impl Step for RustcDocs { +impl CommandLineStep for RustcDocs { type Output = GeneratedTarball; const IS_HOST: bool = true; @@ -423,7 +425,7 @@ pub struct Mingw { target: TargetSelection, } -impl Step for Mingw { +impl CommandLineStep for Mingw { type Output = Option; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -478,7 +480,7 @@ pub struct Rustc { pub target_compiler: Compiler, } -impl Step for Rustc { +impl CommandLineStep for Rustc { type Output = GeneratedTarball; const IS_HOST: bool = true; @@ -707,10 +709,6 @@ pub struct DebuggerScripts { impl Step for DebuggerScripts { type Output = (); - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - fn run(self, builder: &Builder<'_>) { let target = self.target; let sysroot = self.sysroot; @@ -851,7 +849,7 @@ impl Std { } } -impl Step for Std { +impl CommandLineStep for Std { type Output = Option; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -916,7 +914,7 @@ impl RustcDev { } } -impl Step for RustcDev { +impl CommandLineStep for RustcDev { type Output = Option; const IS_HOST: bool = true; @@ -984,7 +982,7 @@ pub struct Analysis { target: TargetSelection, } -impl Step for Analysis { +impl CommandLineStep for Analysis { type Output = Option; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1157,7 +1155,7 @@ fn copy_src_dirs( #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Src; -impl Step for Src { +impl CommandLineStep for Src { /// The output path of the src installer tarball type Output = GeneratedTarball; const IS_HOST: bool = true; @@ -1232,7 +1230,7 @@ impl Step for Src { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct PlainSourceTarball; -impl Step for PlainSourceTarball { +impl CommandLineStep for PlainSourceTarball { /// Produces the location of the tarball generated type Output = GeneratedTarball; const IS_HOST: bool = true; @@ -1282,7 +1280,7 @@ impl Step for PlainSourceTarball { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct PlainSourceTarballGpl; -impl Step for PlainSourceTarballGpl { +impl CommandLineStep for PlainSourceTarballGpl { /// Produces the location of the tarball generated type Output = GeneratedTarball; const IS_HOST: bool = true; @@ -1417,7 +1415,7 @@ pub struct Cargo { pub target: TargetSelection, } -impl Step for Cargo { +impl CommandLineStep for Cargo { type Output = Option; const IS_HOST: bool = true; @@ -1477,7 +1475,7 @@ pub struct RustAnalyzer { pub target: TargetSelection, } -impl Step for RustAnalyzer { +impl CommandLineStep for RustAnalyzer { type Output = Option; const IS_HOST: bool = true; @@ -1522,7 +1520,7 @@ pub struct Clippy { pub target: TargetSelection, } -impl Step for Clippy { +impl CommandLineStep for Clippy { type Output = Option; const IS_HOST: bool = true; @@ -1570,7 +1568,7 @@ pub struct Miri { pub target: TargetSelection, } -impl Step for Miri { +impl CommandLineStep for Miri { type Output = Option; const IS_HOST: bool = true; @@ -1620,7 +1618,7 @@ pub struct CraneliftCodegenBackend { pub target: TargetSelection, } -impl Step for CraneliftCodegenBackend { +impl CommandLineStep for CraneliftCodegenBackend { type Output = Option; const IS_HOST: bool = true; @@ -1694,7 +1692,7 @@ pub struct GccCodegenBackend { pub target: TargetSelection, } -impl Step for GccCodegenBackend { +impl CommandLineStep for GccCodegenBackend { type Output = Option; const IS_HOST: bool = true; @@ -1798,7 +1796,7 @@ pub struct Rustfmt { pub target: TargetSelection, } -impl Step for Rustfmt { +impl CommandLineStep for Rustfmt { type Output = Option; const IS_HOST: bool = true; @@ -1842,7 +1840,7 @@ pub struct Extended { target: TargetSelection, } -impl Step for Extended { +impl CommandLineStep for Extended { type Output = (); const IS_HOST: bool = true; @@ -2616,7 +2614,7 @@ pub struct LlvmTools { pub target: TargetSelection, } -impl Step for LlvmTools { +impl CommandLineStep for LlvmTools { type Output = Option; const IS_HOST: bool = true; @@ -2721,7 +2719,7 @@ pub struct LlvmBitcodeLinker { pub target: TargetSelection, } -impl Step for LlvmBitcodeLinker { +impl CommandLineStep for LlvmBitcodeLinker { type Output = Option; const IS_HOST: bool = true; @@ -2770,7 +2768,7 @@ pub struct Enzyme { pub target: TargetSelection, } -impl Step for Enzyme { +impl CommandLineStep for Enzyme { type Output = Option; const IS_HOST: bool = true; @@ -2824,7 +2822,7 @@ pub struct RustDev { pub target: TargetSelection, } -impl Step for RustDev { +impl CommandLineStep for RustDev { type Output = Option; const IS_HOST: bool = true; @@ -2932,7 +2930,7 @@ pub struct Bootstrap { target: TargetSelection, } -impl Step for Bootstrap { +impl CommandLineStep for Bootstrap { type Output = Option; const IS_HOST: bool = true; @@ -2976,7 +2974,7 @@ pub struct BuildManifest { target: TargetSelection, } -impl Step for BuildManifest { +impl CommandLineStep for BuildManifest { type Output = GeneratedTarball; const IS_HOST: bool = true; @@ -3016,7 +3014,7 @@ pub struct ReproducibleArtifacts { target: TargetSelection, } -impl Step for ReproducibleArtifacts { +impl CommandLineStep for ReproducibleArtifacts { type Output = Option; const IS_HOST: bool = true; @@ -3065,7 +3063,7 @@ pub struct GccDev { target: TargetSelection, } -impl Step for GccDev { +impl CommandLineStep for GccDev { type Output = GeneratedTarball; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -3100,7 +3098,7 @@ pub struct Gcc { target: TargetSelection, } -impl Step for Gcc { +impl CommandLineStep for Gcc { type Output = Option; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 99e2c61e442ff..f7ef0a93c1446 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -16,7 +16,8 @@ use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, SourceType, Tool, prepare_tool_cargo, }; use crate::core::builder::{ - self, Builder, Compiler, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, + self, Builder, CommandLineStep, Compiler, Kind, RunConfig, ShouldRun, Step, StepMetadata, + crate_description, }; use crate::core::config::{Config, TargetSelection}; use crate::helpers::{submodule_path_of, symlink_dir, t, up_to_date}; @@ -30,7 +31,7 @@ macro_rules! book { target: TargetSelection, } - impl Step for $name { + impl CommandLineStep for $name { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -86,7 +87,7 @@ pub struct UnstableBook { target: TargetSelection, } -impl Step for UnstableBook { +impl CommandLineStep for UnstableBook { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -128,7 +129,7 @@ impl Step for UnstableBook { } #[derive(Debug, Clone, Hash, PartialEq, Eq)] -struct RustbookSrc { +struct RustbookSrc { target: TargetSelection, name: String, src: PathBuf, @@ -138,13 +139,9 @@ struct RustbookSrc { build_compiler: Option, } -impl Step for RustbookSrc

{ +impl Step for RustbookSrc

{ type Output = (); - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - /// Invoke `rustbook` for `target` for the doc book `name` from the `src` path. /// /// This will not actually generate any documentation if the documentation has @@ -229,7 +226,7 @@ pub struct TheBook { target: TargetSelection, } -impl Step for TheBook { +impl CommandLineStep for TheBook { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -355,7 +352,7 @@ pub struct Standalone { target: TargetSelection, } -impl Step for Standalone { +impl CommandLineStep for Standalone { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -467,7 +464,7 @@ pub struct Releases { target: TargetSelection, } -impl Step for Releases { +impl CommandLineStep for Releases { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -577,15 +574,6 @@ pub struct SharedAssets { impl Step for SharedAssets { type Output = SharedAssetsPaths; - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - // Other tasks depend on this, no need to execute it on its own - run.never() - } - - fn is_default_step(_builder: &Builder<'_>) -> bool { - false - } - /// Generate shared resources used by other pieces of documentation. fn run(self, builder: &Builder<'_>) -> Self::Output { let out = builder.doc_out(self.target); @@ -654,7 +642,7 @@ impl Std { } } -impl Step for Std { +impl CommandLineStep for Std { /// Path to a directory with the built documentation. type Output = PathBuf; @@ -911,7 +899,7 @@ impl Rustc { } } -impl Step for Rustc { +impl CommandLineStep for Rustc { type Output = (); const IS_HOST: bool = true; @@ -1059,7 +1047,7 @@ macro_rules! tool_doc { target: TargetSelection, } - impl Step for $tool { + impl CommandLineStep for $tool { type Output = (); const IS_HOST: bool = true; @@ -1262,7 +1250,7 @@ pub struct ErrorIndex { compilers: RustcPrivateCompilers, } -impl Step for ErrorIndex { +impl CommandLineStep for ErrorIndex { type Output = (); const IS_HOST: bool = true; @@ -1310,7 +1298,7 @@ pub struct UnstableBookGen { target: TargetSelection, } -impl Step for UnstableBookGen { +impl CommandLineStep for UnstableBookGen { type Output = (); const IS_HOST: bool = true; @@ -1388,7 +1376,7 @@ impl RustcBook { } } -impl Step for RustcBook { +impl CommandLineStep for RustcBook { type Output = (); const IS_HOST: bool = true; @@ -1494,7 +1482,7 @@ pub struct Reference { target: TargetSelection, } -impl Step for Reference { +impl CommandLineStep for Reference { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index 992279fe1e133..ed0e57b884aa8 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -15,7 +15,7 @@ use std::sync::OnceLock; use build_helper::git::PathFreshness; -use crate::core::builder::{Builder, Cargo, Kind, RunConfig, ShouldRun, Step}; +use crate::core::builder::{Builder, Cargo, CommandLineStep, Kind, RunConfig, ShouldRun}; use crate::core::config::TargetSelection; use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; @@ -76,7 +76,7 @@ impl GccOutput { } } -impl Step for Gcc { +impl CommandLineStep for Gcc { type Output = GccOutput; const IS_HOST: bool = true; diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index d23fe029bcc7e..7950caa18ce71 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -8,7 +8,7 @@ use std::{env, fs}; use crate::core::build_steps::dist; use crate::core::build_steps::tool::RustcPrivateCompilers; -use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; +use crate::core::builder::{Builder, CommandLineStep, RunConfig, ShouldRun}; use crate::core::config::{Config, TargetSelection}; use crate::utils::exec::command; use crate::utils::helpers::t; @@ -178,7 +178,7 @@ macro_rules! install { } } - impl Step for $name { + impl CommandLineStep for $name { type Output = (); const IS_HOST: bool = $IS_HOST; $(const $c: bool = true;)* @@ -319,7 +319,7 @@ pub struct Src { stage: u32, } -impl Step for Src { +impl CommandLineStep for Src { type Output = (); const IS_HOST: bool = true; diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index be70d9f4f106c..ff96f341966b7 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -17,7 +17,7 @@ use std::{env, fs}; use build_helper::git::PathFreshness; use crate::core::build_steps::llvm; -use crate::core::builder::{Builder, RunConfig, ShouldRun, Step, StepMetadata}; +use crate::core::builder::{Builder, CommandLineStep, RunConfig, ShouldRun, StepMetadata}; use crate::core::config::{Config, LlvmPgoGenerationMode, TargetSelection}; use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; @@ -272,7 +272,7 @@ pub struct Llvm { pub target: TargetSelection, } -impl Step for Llvm { +impl CommandLineStep for Llvm { type Output = LlvmResult; const IS_HOST: bool = true; @@ -962,7 +962,7 @@ pub struct OmpOffload { pub target: TargetSelection, } -impl Step for OmpOffload { +impl CommandLineStep for OmpOffload { type Output = BuiltOmpOffload; const IS_HOST: bool = true; @@ -1142,7 +1142,7 @@ pub struct Enzyme { pub target: TargetSelection, } -impl Step for Enzyme { +impl CommandLineStep for Enzyme { type Output = BuiltEnzyme; const IS_HOST: bool = true; @@ -1278,7 +1278,7 @@ pub struct Lld { pub target: TargetSelection, } -impl Step for Lld { +impl CommandLineStep for Lld { type Output = PathBuf; const IS_HOST: bool = true; @@ -1402,7 +1402,7 @@ pub struct Sanitizers { pub target: TargetSelection, } -impl Step for Sanitizers { +impl CommandLineStep for Sanitizers { type Output = Vec; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1598,7 +1598,7 @@ pub struct CrtBeginEnd { pub target: TargetSelection, } -impl Step for CrtBeginEnd { +impl CommandLineStep for CrtBeginEnd { type Output = PathBuf; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1676,7 +1676,7 @@ pub struct Libunwind { pub target: TargetSelection, } -impl Step for Libunwind { +impl CommandLineStep for Libunwind { type Output = PathBuf; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 1d704230902e4..c062144aa5c41 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -12,7 +12,7 @@ use crate::core::build_steps::dist::distdir; use crate::core::build_steps::test; use crate::core::build_steps::tool::{self, RustcPrivateCompilers, SourceType, Tool}; use crate::core::build_steps::vendor::{VENDOR_DIR, Vendor, default_paths_to_vendor}; -use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata}; +use crate::core::builder::{Builder, CommandLineStep, Kind, RunConfig, ShouldRun, StepMetadata}; use crate::core::config::TargetSelection; use crate::core::config::flags::{get_completion, top_level_help}; use crate::utils::exec::command; @@ -21,7 +21,7 @@ use crate::{Mode, exit, t}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct BuildManifest; -impl Step for BuildManifest { +impl CommandLineStep for BuildManifest { type Output = (); const IS_HOST: bool = true; @@ -62,7 +62,7 @@ impl Step for BuildManifest { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct BumpStage0; -impl Step for BumpStage0 { +impl CommandLineStep for BumpStage0 { type Output = (); const IS_HOST: bool = true; @@ -84,7 +84,7 @@ impl Step for BumpStage0 { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct ReplaceVersionPlaceholder; -impl Step for ReplaceVersionPlaceholder { +impl CommandLineStep for ReplaceVersionPlaceholder { type Output = (); const IS_HOST: bool = true; @@ -117,7 +117,7 @@ pub struct Miri { target: TargetSelection, } -impl Step for Miri { +impl CommandLineStep for Miri { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -195,7 +195,7 @@ impl Step for Miri { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct CollectLicenseMetadata; -impl Step for CollectLicenseMetadata { +impl CommandLineStep for CollectLicenseMetadata { type Output = PathBuf; const IS_HOST: bool = true; @@ -236,7 +236,7 @@ impl Step for CollectLicenseMetadata { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct GenerateCopyright; -impl Step for GenerateCopyright { +impl CommandLineStep for GenerateCopyright { type Output = Vec; const IS_HOST: bool = true; @@ -303,7 +303,7 @@ impl Step for GenerateCopyright { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct GenerateWindowsSys; -impl Step for GenerateWindowsSys { +impl CommandLineStep for GenerateWindowsSys { type Output = (); const IS_HOST: bool = true; @@ -339,7 +339,7 @@ pub fn get_completion_paths(builder: &Builder<'_>) -> Vec<(&'static dyn Generato #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GenerateCompletions; -impl Step for GenerateCompletions { +impl CommandLineStep for GenerateCompletions { type Output = (); /// Uses `clap_complete` to generate shell completions. @@ -367,7 +367,7 @@ impl Step for GenerateCompletions { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct UnicodeTableGenerator; -impl Step for UnicodeTableGenerator { +impl CommandLineStep for UnicodeTableGenerator { type Output = (); const IS_HOST: bool = true; @@ -391,7 +391,7 @@ impl Step for UnicodeTableGenerator { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct FeaturesStatusDump; -impl Step for FeaturesStatusDump { +impl CommandLineStep for FeaturesStatusDump { type Output = (); const IS_HOST: bool = true; @@ -426,7 +426,7 @@ pub struct CyclicStep { n: u32, } -impl Step for CyclicStep { +impl CommandLineStep for CyclicStep { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -451,7 +451,7 @@ impl Step for CyclicStep { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct CoverageDump; -impl Step for CoverageDump { +impl CommandLineStep for CoverageDump { type Output = (); const IS_HOST: bool = true; @@ -477,7 +477,7 @@ impl Step for CoverageDump { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Rustfmt; -impl Step for Rustfmt { +impl CommandLineStep for Rustfmt { type Output = (); const IS_HOST: bool = true; @@ -535,7 +535,7 @@ pub fn get_help_path(builder: &Builder<'_>) -> PathBuf { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GenerateHelp; -impl Step for GenerateHelp { +impl CommandLineStep for GenerateHelp { type Output = (); fn run(self, builder: &Builder<'_>) { diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 8e65cb18d58fb..7b0bd1e3067fb 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -18,7 +18,7 @@ use std::{fmt, fs, io}; use serde_derive::{Deserialize, Serialize}; use sha2::Digest; -use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; +use crate::core::builder::{Builder, CommandLineStep, RunConfig, ShouldRun}; use crate::utils::change_tracker::CONFIG_CHANGE_HISTORY; use crate::utils::exec::command; use crate::utils::helpers::{self, hex_encode}; @@ -104,7 +104,7 @@ impl fmt::Display for Profile { } } -impl Step for Profile { +impl CommandLineStep for Profile { type Output = (); fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -235,7 +235,7 @@ fn setup_config_toml(path: &Path, profile: Profile, config: &Config) { /// Creates a toolchain link for stage1 using `rustup` #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct Link; -impl Step for Link { +impl CommandLineStep for Link { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -457,7 +457,7 @@ fn prompt_user(prompt: &str) -> io::Result> { #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct Hook; -impl Step for Hook { +impl CommandLineStep for Hook { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -645,7 +645,7 @@ Select which editor you would like to set up [default: None]: "; #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct Editor; -impl Step for Editor { +impl CommandLineStep for Editor { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { diff --git a/src/bootstrap/src/core/build_steps/synthetic_targets.rs b/src/bootstrap/src/core/build_steps/synthetic_targets.rs index 964ccf1c31088..88f04dcb27c6e 100644 --- a/src/bootstrap/src/core/build_steps/synthetic_targets.rs +++ b/src/bootstrap/src/core/build_steps/synthetic_targets.rs @@ -8,7 +8,7 @@ //! that calls create_synthetic_target. use crate::Compiler; -use crate::core::builder::{Builder, ShouldRun, Step}; +use crate::core::builder::{Builder, Step}; use crate::core::config::TargetSelection; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -20,14 +20,6 @@ pub(crate) struct MirOptPanicAbortSyntheticTarget { impl Step for MirOptPanicAbortSyntheticTarget { type Output = TargetSelection; - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - - fn is_default_step(_builder: &Builder<'_>) -> bool { - true - } - fn run(self, builder: &Builder<'_>) -> Self::Output { create_synthetic_target(builder, self.compiler, "miropt-abort", self.base, |spec| { spec.insert("panic-strategy".into(), "abort".into()); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 093a45c1e39f6..e51affa7875b7 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -28,8 +28,8 @@ use crate::core::build_steps::tool::{ use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, dist, llvm}; use crate::core::builder::{ - self, Alias, Builder, Compiler, Kind, RunConfig, ShouldRun, Step, StepMetadata, - crate_description, + self, Alias, Builder, CommandLineStep, Compiler, Kind, RunConfig, ShouldRun, Step, + StepMetadata, crate_description, }; use crate::core::config::TargetSelection; use crate::core::config::flags::{Subcommand, get_completion, top_level_help}; @@ -54,7 +54,7 @@ pub struct CrateBootstrap { host: TargetSelection, } -impl Step for CrateBootstrap { +impl CommandLineStep for CrateBootstrap { type Output = (); const IS_HOST: bool = true; @@ -123,7 +123,7 @@ pub struct Linkcheck { host: TargetSelection, } -impl Step for Linkcheck { +impl CommandLineStep for Linkcheck { type Output = (); const IS_HOST: bool = true; @@ -222,7 +222,7 @@ pub struct HtmlCheck { target: TargetSelection, } -impl Step for HtmlCheck { +impl CommandLineStep for HtmlCheck { type Output = (); const IS_HOST: bool = true; @@ -275,7 +275,7 @@ pub struct Cargotest { host: TargetSelection, } -impl Step for Cargotest { +impl CommandLineStep for Cargotest { type Output = (); const IS_HOST: bool = true; @@ -352,7 +352,7 @@ impl Cargo { const CRATE_PATH: &str = "src/tools/cargo"; } -impl Step for Cargo { +impl CommandLineStep for Cargo { type Output = (); const IS_HOST: bool = true; @@ -454,7 +454,7 @@ pub struct RustAnalyzer { compilers: RustcPrivateCompilers, } -impl Step for RustAnalyzer { +impl CommandLineStep for RustAnalyzer { type Output = (); const IS_HOST: bool = true; @@ -578,7 +578,7 @@ pub struct Rustfmt { compilers: RustcPrivateCompilers, } -impl Step for Rustfmt { +impl CommandLineStep for Rustfmt { type Output = (); const IS_HOST: bool = true; @@ -683,7 +683,7 @@ impl Miri { } } -impl Step for Miri { +impl CommandLineStep for Miri { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -801,7 +801,7 @@ pub struct CargoMiri { target: TargetSelection, } -impl Step for CargoMiri { +impl CommandLineStep for CargoMiri { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -881,7 +881,7 @@ pub struct CompiletestTest { host: TargetSelection, } -impl Step for CompiletestTest { +impl CommandLineStep for CompiletestTest { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -947,7 +947,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StdarchVerify; -impl Step for StdarchVerify { +impl CommandLineStep for StdarchVerify { type Output = (); const IS_HOST: bool = true; @@ -1005,7 +1005,7 @@ pub struct IntrinsicTest { host: TargetSelection, } -impl Step for IntrinsicTest { +impl CommandLineStep for IntrinsicTest { type Output = (); const IS_HOST: bool = true; @@ -1164,7 +1164,7 @@ pub struct Clippy { compilers: RustcPrivateCompilers, } -impl Step for Clippy { +impl CommandLineStep for Clippy { type Output = (); const IS_HOST: bool = true; @@ -1281,7 +1281,7 @@ pub struct RustdocTheme { test_compiler: Compiler, } -impl Step for RustdocTheme { +impl CommandLineStep for RustdocTheme { type Output = (); const IS_HOST: bool = true; @@ -1334,7 +1334,7 @@ pub struct RustdocJSStd { target: TargetSelection, } -impl Step for RustdocJSStd { +impl CommandLineStep for RustdocJSStd { type Output = (); const IS_HOST: bool = true; @@ -1411,7 +1411,7 @@ pub struct RustdocJSNotStd { pub compiler: Compiler, } -impl Step for RustdocJSNotStd { +impl CommandLineStep for RustdocJSNotStd { type Output = (); const IS_HOST: bool = true; @@ -1479,7 +1479,7 @@ pub struct RustdocGUI { target: TargetSelection, } -impl Step for RustdocGUI { +impl CommandLineStep for RustdocGUI { type Output = (); const IS_HOST: bool = true; @@ -1584,7 +1584,7 @@ impl Step for RustdocGUI { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Tidy; -impl Step for Tidy { +impl CommandLineStep for Tidy { type Output = (); const IS_HOST: bool = true; @@ -1721,7 +1721,7 @@ pub struct CrateRunMakeSupport { host: TargetSelection, } -impl Step for CrateRunMakeSupport { +impl CommandLineStep for CrateRunMakeSupport { type Output = (); const IS_HOST: bool = true; @@ -1767,7 +1767,7 @@ pub struct CrateBuildHelper { host: TargetSelection, } -impl Step for CrateBuildHelper { +impl CommandLineStep for CrateBuildHelper { type Output = (); const IS_HOST: bool = true; @@ -1833,7 +1833,7 @@ macro_rules! test { target: TargetSelection, } - impl Step for $name { + impl CommandLineStep for $name { type Output = (); const IS_HOST: bool = (const { #[allow(unused_assignments, unused_mut)] @@ -1994,7 +1994,7 @@ impl Coverage { &[CompiletestMode::CoverageMap, CompiletestMode::CoverageRun]; } -impl Step for Coverage { +impl CommandLineStep for Coverage { type Output = (); /// Compiletest will automatically skip the "coverage-run" tests if necessary. const IS_HOST: bool = false; @@ -2091,7 +2091,7 @@ pub struct MirOpt { pub target: TargetSelection, } -impl Step for MirOpt { +impl CommandLineStep for MirOpt { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -2164,10 +2164,6 @@ struct Compiletest { impl Step for Compiletest { type Output = (); - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - fn run(self, builder: &Builder<'_>) { if builder.test_target == TestTarget::DocOnly { return; @@ -2888,11 +2884,6 @@ struct BookTest { impl Step for BookTest { type Output = (); - const IS_HOST: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } fn run(self, builder: &Builder<'_>) { // External docs are different from local because: @@ -3051,7 +3042,7 @@ macro_rules! test_book { test_compiler: Compiler, } - impl Step for $name { + impl CommandLineStep for $name { type Output = (); const IS_HOST: bool = true; @@ -3114,7 +3105,7 @@ pub struct ErrorIndex { compilers: RustcPrivateCompilers, } -impl Step for ErrorIndex { +impl CommandLineStep for ErrorIndex { type Output = (); const IS_HOST: bool = true; @@ -3208,7 +3199,7 @@ pub struct CrateLibrustc { crates: Vec, } -impl Step for CrateLibrustc { +impl CommandLineStep for CrateLibrustc { type Output = (); const IS_HOST: bool = true; @@ -3369,7 +3360,7 @@ pub struct Crate { crates: Vec, } -impl Step for Crate { +impl CommandLineStep for Crate { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -3517,7 +3508,7 @@ pub struct CrateRustdoc { host: TargetSelection, } -impl Step for CrateRustdoc { +impl CommandLineStep for CrateRustdoc { type Output = (); const IS_HOST: bool = true; @@ -3622,7 +3613,7 @@ pub struct CrateRustdocJsonTypes { target: TargetSelection, } -impl Step for CrateRustdocJsonTypes { +impl CommandLineStep for CrateRustdocJsonTypes { type Output = (); const IS_HOST: bool = true; @@ -3698,10 +3689,6 @@ pub struct RemoteCopyLibs { impl Step for RemoteCopyLibs { type Output = (); - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - fn run(self, builder: &Builder<'_>) { let build_compiler = self.build_compiler; let target = self.target; @@ -3740,7 +3727,7 @@ impl Step for RemoteCopyLibs { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Distcheck; -impl Step for Distcheck { +impl CommandLineStep for Distcheck { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -3864,7 +3851,7 @@ fn distcheck_rustc_dev(builder: &Builder<'_>, dir: &Path) { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct BootstrapPy; -impl Step for BootstrapPy { +impl CommandLineStep for BootstrapPy { type Output = (); const IS_HOST: bool = true; @@ -3903,7 +3890,7 @@ impl Step for BootstrapPy { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Bootstrap; -impl Step for Bootstrap { +impl CommandLineStep for Bootstrap { type Output = (); const IS_HOST: bool = true; @@ -3973,7 +3960,7 @@ pub struct TierCheck { test_compiler: Compiler, } -impl Step for TierCheck { +impl CommandLineStep for TierCheck { type Output = (); const IS_HOST: bool = true; @@ -4025,7 +4012,7 @@ pub struct LintDocs { target: TargetSelection, } -impl Step for LintDocs { +impl CommandLineStep for LintDocs { type Output = (); const IS_HOST: bool = true; @@ -4071,7 +4058,7 @@ impl Step for LintDocs { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RustInstaller; -impl Step for RustInstaller { +impl CommandLineStep for RustInstaller { type Output = (); const IS_HOST: bool = true; @@ -4131,7 +4118,7 @@ pub struct TestHelpers { pub target: TargetSelection, } -impl Step for TestHelpers { +impl CommandLineStep for TestHelpers { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -4215,7 +4202,7 @@ pub struct CodegenCranelift { target: TargetSelection, } -impl Step for CodegenCranelift { +impl CommandLineStep for CodegenCranelift { type Output = (); const IS_HOST: bool = true; @@ -4336,7 +4323,7 @@ pub struct CodegenGCC { target: TargetSelection, } -impl Step for CodegenGCC { +impl CommandLineStep for CodegenGCC { type Output = (); const IS_HOST: bool = true; @@ -4467,7 +4454,7 @@ pub struct TestFloatParse { target: TargetSelection, } -impl Step for TestFloatParse { +impl CommandLineStep for TestFloatParse { type Output = (); const IS_HOST: bool = true; @@ -4545,7 +4532,7 @@ impl Step for TestFloatParse { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct CollectLicenseMetadata; -impl Step for CollectLicenseMetadata { +impl CommandLineStep for CollectLicenseMetadata { type Output = PathBuf; const IS_HOST: bool = true; @@ -4579,7 +4566,7 @@ pub struct RemoteTestClientTests { host: TargetSelection, } -impl Step for RemoteTestClientTests { +impl CommandLineStep for RemoteTestClientTests { type Output = (); const IS_HOST: bool = true; diff --git a/src/bootstrap/src/core/build_steps/test/failed_tests.rs b/src/bootstrap/src/core/build_steps/test/failed_tests.rs index 8be6e50fd3db1..abcfbe31bb2c3 100644 --- a/src/bootstrap/src/core/build_steps/test/failed_tests.rs +++ b/src/bootstrap/src/core/build_steps/test/failed_tests.rs @@ -3,7 +3,7 @@ use std::fs::{self, File}; use std::io::{BufRead, BufReader, ErrorKind}; use std::path::{Path, PathBuf}; -use crate::core::builder::{Builder, ShouldRun, Step}; +use crate::core::builder::{Builder, Step}; use crate::t; #[derive(Clone)] @@ -32,10 +32,6 @@ pub struct SetupFailedTestsFile; impl Step for SetupFailedTestsFile { type Output = RecordFailedTests; - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - fn run(self, builder: &Builder<'_>) -> Self::Output { if !builder.config.cmd.record() || builder.config.dry_run() { return RecordFailedTests { failed_tests_path: None }; diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index a6ecdea06623f..89143c8e74333 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -18,8 +18,8 @@ use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, llvm}; use crate::core::builder; use crate::core::builder::{ - Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step, StepMetadata, apply_pgo, - cargo_profile_var, + Builder, Cargo as CargoCommand, CommandLineStep, RunConfig, ShouldRun, Step, StepMetadata, + apply_pgo, cargo_profile_var, }; use crate::core::config::{DebuginfoLevel, OverrideAllocator, RustcLto, TargetSelection}; use crate::utils::exec::{BootstrapCommand, command}; @@ -69,10 +69,6 @@ pub struct ToolBuildResult { impl Step for ToolBuild { type Output = ToolBuildResult; - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - /// Builds a tool in `src/tools` /// /// This will build the specified tool with the specified `host` compiler in @@ -432,7 +428,7 @@ macro_rules! bootstrap_tool { pub target: TargetSelection, } - impl Step for $name { + impl CommandLineStep for $name { type Output = ToolBuildResult; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -535,7 +531,7 @@ pub struct RustcPerf { pub target: TargetSelection, } -impl Step for RustcPerf { +impl CommandLineStep for RustcPerf { /// Path to the built `collector` binary. type Output = ToolBuildResult; @@ -596,7 +592,7 @@ impl ErrorIndex { } } -impl Step for ErrorIndex { +impl CommandLineStep for ErrorIndex { type Output = ToolBuildResult; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -653,7 +649,7 @@ pub struct RemoteTestServer { pub target: TargetSelection, } -impl Step for RemoteTestServer { +impl CommandLineStep for RemoteTestServer { type Output = ToolBuildResult; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -701,7 +697,7 @@ pub struct Rustdoc { pub target_compiler: Compiler, } -impl Step for Rustdoc { +impl CommandLineStep for Rustdoc { /// Path to the built rustdoc binary. type Output = PathBuf; @@ -828,7 +824,7 @@ impl Cargo { } } -impl Step for Cargo { +impl CommandLineStep for Cargo { type Output = ToolBuildResult; const IS_HOST: bool = true; @@ -906,7 +902,7 @@ impl LldWrapper { } } -impl Step for LldWrapper { +impl CommandLineStep for LldWrapper { type Output = BuiltLldWrapper; const IS_HOST: bool = true; @@ -998,7 +994,7 @@ impl WasmComponentLd { } } -impl Step for WasmComponentLd { +impl CommandLineStep for WasmComponentLd { type Output = ToolBuildResult; const IS_HOST: bool = true; @@ -1052,7 +1048,7 @@ impl RustAnalyzer { pub const ALLOW_FEATURES: &'static str = "rustc_private,proc_macro_internals,proc_macro_diagnostic,proc_macro_span,proc_macro_span_shrink,proc_macro_def_site,new_zeroed_alloc"; } -impl Step for RustAnalyzer { +impl CommandLineStep for RustAnalyzer { type Output = ToolBuildResult; const IS_HOST: bool = true; @@ -1106,7 +1102,7 @@ impl RustAnalyzerProcMacroSrv { } } -impl Step for RustAnalyzerProcMacroSrv { +impl CommandLineStep for RustAnalyzerProcMacroSrv { type Output = ToolBuildResult; const IS_HOST: bool = true; @@ -1195,7 +1191,7 @@ impl LlvmBitcodeLinker { } } -impl Step for LlvmBitcodeLinker { +impl CommandLineStep for LlvmBitcodeLinker { type Output = ToolBuildResult; const IS_HOST: bool = true; @@ -1248,15 +1244,6 @@ pub enum LibcxxVersion { impl Step for LibcxxVersionTool { type Output = LibcxxVersion; - const IS_HOST: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - - fn is_default_step(_builder: &Builder<'_>) -> bool { - false - } fn run(self, builder: &Builder<'_>) -> LibcxxVersion { let out_dir = builder.out.join(self.target.to_string()).join("libcxx-version"); @@ -1312,7 +1299,7 @@ impl BuildManifest { } } -impl Step for BuildManifest { +impl CommandLineStep for BuildManifest { type Output = ToolBuildResult; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1456,7 +1443,7 @@ macro_rules! tool_rustc_extended { } } - impl Step for $name { + impl CommandLineStep for $name { type Output = ToolBuildResult; const IS_HOST: bool = true; diff --git a/src/bootstrap/src/core/build_steps/toolstate.rs b/src/bootstrap/src/core/build_steps/toolstate.rs index fc7acd1fa149b..73b6863248df0 100644 --- a/src/bootstrap/src/core/build_steps/toolstate.rs +++ b/src/bootstrap/src/core/build_steps/toolstate.rs @@ -11,7 +11,7 @@ use std::{env, fmt, fs, time}; use serde_derive::{Deserialize, Serialize}; -use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; +use crate::core::builder::{Builder, CommandLineStep, RunConfig, ShouldRun}; use crate::utils::helpers::{self, t}; // Each cycle is 42 days long (6 weeks); the last week is 35..=42 then. @@ -122,7 +122,7 @@ fn check_changed_files(builder: &Builder<'_>, toolstates: &HashMap, Too #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct ToolStateCheck; -impl Step for ToolStateCheck { +impl CommandLineStep for ToolStateCheck { type Output = (); /// Checks tool state status. diff --git a/src/bootstrap/src/core/build_steps/vendor.rs b/src/bootstrap/src/core/build_steps/vendor.rs index 519f134bb8203..1bf9331500f8b 100644 --- a/src/bootstrap/src/core/build_steps/vendor.rs +++ b/src/bootstrap/src/core/build_steps/vendor.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; use crate::core::build_steps::tool::SUBMODULES_FOR_RUSTBOOK; -use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; +use crate::core::builder::{Builder, CommandLineStep, RunConfig, ShouldRun}; use crate::utils::exec::command; /// The name of the directory where vendored dependencies are stored. @@ -58,7 +58,7 @@ pub(crate) struct Vendor { pub(crate) only_library_workspace: bool, } -impl Step for Vendor { +impl CommandLineStep for Vendor { type Output = VendorOutput; const IS_HOST: bool = true; diff --git a/src/bootstrap/src/core/builder/cli_paths.rs b/src/bootstrap/src/core/builder/cli_paths.rs index b645ce9f417ae..4ad76b29e5246 100644 --- a/src/bootstrap/src/core/builder/cli_paths.rs +++ b/src/bootstrap/src/core/builder/cli_paths.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Debug}; use std::path::PathBuf; -use crate::core::builder::{Builder, Kind, PathSet, ShouldRun, StepDescription}; +use crate::core::builder::{Builder, CommandLineStepDescription, Kind, PathSet, ShouldRun}; #[cfg(test)] mod tests; @@ -93,21 +93,21 @@ impl From for CLIStepPath { } } -/// Combines a `StepDescription` with its corresponding `ShouldRun`. +/// Combines a [`CommandLineStepDescription`] with its corresponding [`ShouldRun`]. struct StepExtra<'a> { - desc: &'a StepDescription, + desc: &'a CommandLineStepDescription, should_run: ShouldRun<'a>, } struct StepToRun<'a> { sort_index: usize, - desc: &'a StepDescription, + desc: &'a CommandLineStepDescription, pathsets: Vec, } pub(crate) fn match_paths_to_steps_and_run( builder: &Builder<'_>, - step_descs: &[StepDescription], + step_descs: &[CommandLineStepDescription], paths: &[PathBuf], ) { // Obtain `ShouldRun` information for each step, so that we know which diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clean.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clean.snap index 71f12ecd501a7..30eb798f39938 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clean.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clean.snap @@ -4,4 +4,4 @@ expression: clean --- [Clean] clean::CleanAll targets: [aarch64-unknown-linux-gnu] - - Set({}) + - Set({clean::default}) diff --git a/src/bootstrap/src/core/builder/cli_paths/tests.rs b/src/bootstrap/src/core/builder/cli_paths/tests.rs index 79137de12981b..465a370ad69f8 100644 --- a/src/bootstrap/src/core/builder/cli_paths/tests.rs +++ b/src/bootstrap/src/core/builder/cli_paths/tests.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use std::sync::{Arc, Mutex}; use crate::Build; -use crate::core::builder::{Builder, StepDescription}; +use crate::core::builder::{Builder, CommandLineStepDescription}; use crate::utils::tests::TestCtx; fn render_steps_for_cli_args(args_str: &str) -> String { @@ -38,7 +38,7 @@ fn render_steps_for_cli_args(args_str: &str) -> String { use std::fmt::Write; let mut buf = buf2.lock().unwrap(); - let StepDescription { name, kind, .. } = step_desc; + let CommandLineStepDescription { name, kind, .. } = step_desc; // Strip boilerplate to make step names easier to read. let name = name.strip_prefix("bootstrap::core::build_steps::").unwrap_or(name); diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 02f18b43b5ba1..50188887a5314 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -75,7 +75,8 @@ pub struct Builder<'a> { /// executed to be logged instead. Used by snapshot tests of command-line /// paths-to-steps handling. #[expect(clippy::type_complexity)] - log_cli_step_for_tests: Option>, + log_cli_step_for_tests: + Option>, } impl Deref for Builder<'_> { @@ -101,8 +102,47 @@ impl dyn AnyDebug { // Feel free to add other `dyn Any` methods as necessary. } +/// A unit of work within bootstrap that is cached to avoid redundant execution. +/// Steps can be performed via [`Builder::ensure`]. +/// +/// Historically, steps also participated in command-line processing. +/// That responsibility has been split off into the larger [`CommandLineStep`] trait, +/// which helper steps don't need to implement. pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash { - /// Result type of `Step::run`. + /// Result type of [`Step::run`]. Stored in the step cache for later lookup. + type Output: Clone; + + /// Executes this step. + /// + /// Called by [`Builder::ensure`] if no cached result was found for this step. + fn run(self, builder: &Builder<'_>) -> Self::Output; + + /// Returns metadata of the step, for tests. + fn metadata(&self) -> Option { + None + } +} + +/// Every [`CommandLineStep`] is also a [`Step`]. +impl Step for S { + type Output = ::Output; + + fn run(self, builder: &Builder<'_>) -> Self::Output { + ::run(self, builder) + } + + fn metadata(&self) -> Option { + ::metadata(self) + } +} + +/// A [`Step`] that can be selected by command-line arguments. +/// +/// A blanket impl allows every [`CommandLineStep`] to be used as a [`Step`]. +/// This is arguably nicer than having it be a subtrait, because it avoids the +/// need for two separate `impl` blocks per command-line-step type. +pub trait CommandLineStep: 'static + Clone + Debug + PartialEq + Eq + Hash { + /// Result type of [`Step::run`]. type Output: Clone; /// If this value is true, then the values of `run.target` passed to the `make_run` function of @@ -134,33 +174,15 @@ pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash { false } - /// Primary function to implement `Step` logic. - /// - /// This function can be triggered in two ways: - /// 1. Directly from [`Builder::execute_cli`]. - /// 2. Indirectly by being called from other `Step`s using [`Builder::ensure`]. - /// - /// When called with [`Builder::execute_cli`] (as done by `Build::build`), this function is executed twice: - /// - First in "dry-run" mode to validate certain things (like cyclic Step invocations, - /// directory creation, etc) super quickly. - /// - Then it's called again to run the actual, very expensive process. - /// - /// When triggered indirectly from other `Step`s, it may still run twice (as dry-run and real mode) - /// depending on the `Step::run` implementation of the caller. - fn run(self, builder: &Builder<'_>) -> Self::Output; - /// Called directly by the bootstrap `Step` handler when not triggered indirectly by other `Step`s using [`Builder::ensure`]. /// For example, `./x.py test bootstrap` runs this for `test::Bootstrap`. Similarly, `./x.py test` runs it for every step /// that is listed by the `describe` macro in [`Builder::get_step_descriptions`]. - fn make_run(_run: RunConfig<'_>) { - // It is reasonable to not have an implementation of make_run for rules - // who do not want to get called from the root context. This means that - // they are likely dependencies (e.g., sysroot creation) or similar, and - // as such calling them from ./x.py isn't logical. - unimplemented!() - } + fn make_run(_run: RunConfig<'_>); + + /// Used as the implementation of [`Step::run`]. + fn run(self, builder: &Builder<'_>) -> Self::Output; - /// Returns metadata of the step, for tests + /// Used as the implementation of [`Step::metadata`]. fn metadata(&self) -> Option { None } @@ -327,7 +349,7 @@ pub fn crate_description(crates: &[impl AsRef]) -> String { descr } -struct StepDescription { +struct CommandLineStepDescription { is_host: bool, should_run: fn(ShouldRun<'_>) -> ShouldRun<'_>, is_default_step_fn: fn(&Builder<'_>) -> bool, @@ -446,9 +468,9 @@ impl PathSet { } } -impl StepDescription { - fn from(kind: Kind) -> StepDescription { - StepDescription { +impl CommandLineStepDescription { + fn from(kind: Kind) -> CommandLineStepDescription { + CommandLineStepDescription { is_host: S::IS_HOST, should_run: S::should_run, is_default_step_fn: S::is_default_step, @@ -617,12 +639,6 @@ impl<'a> ShouldRun<'a> { self } - // allows being more explicit about why should_run in Step returns the value passed to it - pub fn never(mut self) -> ShouldRun<'a> { - self.paths.insert(PathSet::empty()); - self - } - /// Given a set of requested paths, return the subset which match the Step for this `ShouldRun`, /// removing the matches from `paths`. /// @@ -747,16 +763,12 @@ struct Libdir { impl Step for Libdir { type Output = PathBuf; - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - fn run(self, builder: &Builder<'_>) -> PathBuf { let relative_sysroot_libdir = builder.sysroot_libdir_relative(self.compiler); let sysroot = builder.sysroot(self.compiler).join(relative_sysroot_libdir).join("rustlib"); if !builder.config.dry_run() { - // Avoid deleting the `rustlib/` directory we just copied (in `impl Step for + // Avoid deleting the `rustlib/` directory we just copied (in `impl CommandLineStep for // Sysroot`). if !builder.download_rustc() { let sysroot_target_libdir = sysroot.join(self.target).join("lib"); @@ -790,10 +802,10 @@ impl Step for Libdir { pub const STEP_SPAN_TARGET: &str = "STEP"; impl<'a> Builder<'a> { - fn get_step_descriptions(kind: Kind) -> Vec { + fn get_step_descriptions(kind: Kind) -> Vec { macro_rules! describe { ($($rule:ty),+ $(,)?) => {{ - vec![$(StepDescription::from::<$rule>(kind)),+] + vec![$(CommandLineStepDescription::from::<$rule>(kind)),+] }}; } match kind { @@ -1173,7 +1185,7 @@ impl<'a> Builder<'a> { format!("https://doc.rust-lang.org/{channel}") } - fn run_step_descriptions(&self, v: &[StepDescription], paths: &[PathBuf]) { + fn run_step_descriptions(&self, v: &[CommandLineStepDescription], paths: &[PathBuf]) { cli_paths::match_paths_to_steps_and_run(self, v, paths); } @@ -1681,12 +1693,12 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler /// Ensure that a given step is built *only if it's supposed to be built by default*, returning /// its output. This will cache the step, so it's safe (and good!) to call this as often as /// needed to ensure that all dependencies are build. - pub(crate) fn ensure_if_default>( + pub(crate) fn ensure_if_default>( &'a self, step: S, kind: Kind, ) -> Option { - let desc = StepDescription::from::(kind); + let desc = CommandLineStepDescription::from::(kind); let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind)); // Avoid running steps contained in --skip @@ -1701,8 +1713,8 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler } /// Checks if any of the "should_run" paths is in the `Builder` paths. - pub(crate) fn was_invoked_explicitly(&'a self, kind: Kind) -> bool { - let desc = StepDescription::from::(kind); + pub(crate) fn was_invoked_explicitly(&'a self, kind: Kind) -> bool { + let desc = CommandLineStepDescription::from::(kind); let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind)); for path in &self.paths { @@ -1719,7 +1731,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler false } - pub(crate) fn maybe_open_in_browser(&self, path: impl AsRef) { + pub(crate) fn maybe_open_in_browser(&self, path: impl AsRef) { if self.was_invoked_explicitly::(Kind::Doc) { self.open_in_browser(path); } else { diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 25a5238f538ee..1a74dff9d23d8 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -390279b302ca98ae270f434100ae3730531d1246 +da86f4d0726be475afbbffe40cb2f65741c51ad3 diff --git a/src/doc/rustc-dev-guide/src/ast-validation.md b/src/doc/rustc-dev-guide/src/ast-validation.md index 8f10bbecf21d0..45a9d77252815 100644 --- a/src/doc/rustc-dev-guide/src/ast-validation.md +++ b/src/doc/rustc-dev-guide/src/ast-validation.md @@ -1,28 +1,27 @@ # AST validation _AST validation_ is a separate AST pass that visits each -item in the tree and performs simple checks. This pass -doesn't perform any complex analysis, type checking or +item in the tree and performs simple checks. +This pass doesn't perform any complex analysis, type checking or name resolution. -Before performing any validation, the compiler first expands -the macros. Then this pass performs validations to check -that each AST item is in the correct state. And when this pass -is done, the compiler runs the crate resolution pass. +Before performing any validation, the compiler first expands the macros. +Then this pass performs validations to check that each AST item is in the correct state. +And when this pass is done, the compiler runs the crate resolution pass. ## Validations Validations are defined in `AstValidator` type, which -itself is located in `rustc_ast_passes` crate. This -type implements various simple checks which emit errors +itself is located in `rustc_ast_passes` crate. +This type implements various simple checks which emit errors when certain language rules are broken. In addition, `AstValidator` implements `Visitor` trait that defines how to visit AST items (which can be functions, traits, enums, etc). -For each item, visitor performs specific checks. For -example, when visiting a function declaration, +For each item, visitor performs specific checks. +For example, when visiting a function declaration, `AstValidator` checks that the function has: * no more than `u16::MAX` parameters; diff --git a/src/doc/rustc-dev-guide/src/attributes.md b/src/doc/rustc-dev-guide/src/attributes.md index 1056a257c2ee8..e9703d2c6e209 100644 --- a/src/doc/rustc-dev-guide/src/attributes.md +++ b/src/doc/rustc-dev-guide/src/attributes.md @@ -25,6 +25,35 @@ For more information on these attributes, see the chapter about [attribute parsi [attr-parsing-chapter]: ./hir/attribute-parsing.md +### Note on adding new builtin attributes + +

+ +**Warning: Name resolution ambiguity potential when adding new builtin attributes** + +Please note that adding **new builtin attributes** (whose name is not reserved, i.e. a new builtin +attribute whose name does not start with `rustc`), even if *unstable*-gated, can introduce breakage +from name resolution ambiguity in stable code if (1) the stable code has a macro of the same name +which gets re-exported, or (2) or a proc-macro derive helper attribute of the same name. + +Typically, the builtin attributes probably has to start out as `#[rustc_foo]` instead of `#[foo]` to +avoid colliding with user-defined macros and proc-macro helper attributes. +Then, prior to stabilization, +a rename to `#[foo]` should be done separately with a crater run to assess fallout, +with a deliberate breakage FCP proposal for T-lang to consider. + +Remember also that crater is *not* exhaustive and does not contain all existing stable code. + +See: +- [Built-in attributes are treated differently vs prelude attributes, unstable built-in attributes + can name-collide with stable macro, and built-in attributes can break back-compat + #134963](https://github.com/rust-lang/rust/issues/134963) and backlinks within this issue, + including design discussions on how to fix this kind of breakage hazard. +- [Broken build after updating: coverage is ambiguous; ambiguous because of a name conflict with a + builtin attribute #121157](https://github.com/rust-lang/rust/issues/121157). +- [Regression: align is ambiguous #143834](https://github.com/rust-lang/rust/issues/143834). +
+ ## 'Non-builtin'/'active' attributes These attributes are defined by a crate - either the standard library, or a proc-macro crate. diff --git a/src/doc/rustc-dev-guide/src/const-eval.md b/src/doc/rustc-dev-guide/src/const-eval.md index a3fee034ec6ed..8ffd7d5ba47de 100644 --- a/src/doc/rustc-dev-guide/src/const-eval.md +++ b/src/doc/rustc-dev-guide/src/const-eval.md @@ -1,8 +1,9 @@ # Constant Evaluation -Constant evaluation is the process of computing values at compile time. For a -specific item (constant/static/array length) this happens after the MIR for the -item is borrow-checked and optimized. In many cases trying to const evaluate an +Constant evaluation is the process of computing values at compile time. +For a specific item (constant/static/array length) this happens after the MIR for the +item is borrow-checked and optimized. +In many cases trying to const evaluate an item will trigger the computation of its MIR for the first time. Prominent examples are: @@ -11,14 +12,12 @@ Prominent examples are: * Array length * needs to be known to reserve stack or heap space * Enum variant discriminants - * needs to be known to prevent two variants from having the same - discriminant + * needs to be known to prevent two variants from having the same discriminant * Patterns * need to be known to check for overlapping patterns Additionally constant evaluation can be used to reduce the workload or binary -size at runtime by precomputing complex operations at compile time and only -storing the result. +size at runtime by precomputing complex operations at compile time and only storing the result. All uses of constant evaluation can either be categorized as "influencing the type system" (array lengths, enum variant discriminants, const generic parameters), or as solely being @@ -37,7 +36,8 @@ They're the wrappers of the `const_eval` query. The `const_eval_*` functions use a [`ParamEnv`](./typing-parameter-envs.md) of environment in which the constant is evaluated (e.g. the function within which the constant is used) -and a [`GlobalId`]. The `GlobalId` is made up of an `Instance` referring to a constant +and a [`GlobalId`]. +The `GlobalId` is made up of an `Instance` referring to a constant or static or of an `Instance` of a function and an index into the function's `Promoted` table. Constant evaluation returns an [`EvalToValTreeResult`] for type system constants diff --git a/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md b/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md index 101c793525246..9acc94a174ed5 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md @@ -3,14 +3,13 @@ **Github Label:** None
**Ping command:** `@rustbot ping gpu-target` -This notification group deals with linker related issues and their integration -within the compiler. +This notification group deals with linker-related issues and their integration within the compiler. The group also has an associated Zulip stream ([`#t-compiler/gpgpu-backend`]) where people can go to ask questions and discuss GPU-related topics and issues. -if you're interested in participating, feel free to sign up for this group! To -do so, open a PR against the [rust-lang/team] repository and add your GitHub +if you're interested in participating, feel free to sign up for this group! +To do so, open a PR against the [rust-lang/team] repository and add your GitHub user to [this file][gpu-target-team]. [`#t-compiler/gpgpu-backend`]: https://rust-lang.zulipchat.com/#narrow/channel/422870-t-compiler.2Fgpgpu-backend diff --git a/src/doc/rustc-dev-guide/src/notification-groups/wasi.md b/src/doc/rustc-dev-guide/src/notification-groups/wasi.md index 93962a54fdfb7..a38528e2d9b4e 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/wasi.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/wasi.md @@ -14,8 +14,8 @@ where people can go to pose questions and discuss WASI-specific topics. So, if you are interested in participating, please sign up for the WASI group! To do so, open a PR against the [rust-lang/team] repository. -Just [follow this example][eg], but change the username to your own! +Just follow [this example], but change the username to your own! [`#t-compiler/wasm`]: https://rust-lang.zulipchat.com/#narrow/stream/463513-t-compiler.2Fwasm [rust-lang/team]: https://github.com/rust-lang/team -[eg]: https://github.com/rust-lang/team/pull/1580 +[this example]: https://github.com/rust-lang/team/pull/1580 diff --git a/src/doc/rustc-dev-guide/src/notification-groups/wasm.md b/src/doc/rustc-dev-guide/src/notification-groups/wasm.md index 12e457546ba93..f903bc7114a71 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/wasm.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/wasm.md @@ -10,14 +10,12 @@ WebAssembly-related issues as well as suggestions on how to resolve interesting questions regarding our WASM support. The group also has an associated Zulip channel ([`#t-compiler/wasm`]) -where people can go to pose questions and discuss WASM-specific -topics. +where people can go to pose questions and discuss WASM-specific topics. -So, if you are interested in participating, please sign up for the -WASM group! To do so, open a PR against the [rust-lang/team] -repository. Just [follow this example][eg], but change the username to -your own! +So, if you are interested in participating, please sign up for the WASM group! +To do so, open a PR against the [rust-lang/team] repository. +Just follow [this example], but change the username to your own! [`#t-compiler/wasm`]: https://rust-lang.zulipchat.com/#narrow/stream/463513-t-compiler.2Fwasm [rust-lang/team]: https://github.com/rust-lang/team -[eg]: https://github.com/rust-lang/team/pull/1581 +[this example]: https://github.com/rust-lang/team/pull/1581 diff --git a/src/doc/rustc-dev-guide/src/pat-exhaustive-checking.md b/src/doc/rustc-dev-guide/src/pat-exhaustive-checking.md index e953931aa78c2..b7edb51ac8545 100644 --- a/src/doc/rustc-dev-guide/src/pat-exhaustive-checking.md +++ b/src/doc/rustc-dev-guide/src/pat-exhaustive-checking.md @@ -1,7 +1,7 @@ # Pattern and exhaustiveness checking -In Rust, pattern matching and bindings have a few very helpful properties. The -compiler will check that bindings are irrefutable when made and that match arms +In Rust, pattern matching and bindings have a few very helpful properties. +The compiler will check that bindings are irrefutable when made and that match arms are exhaustive. ## Pattern usefulness @@ -35,8 +35,7 @@ match x { } ``` -Thus usefulness is used for two purposes: -detecting unreachable code (which is useful to the user), +Thus usefulness is used for two purposes: detecting unreachable code (which is useful to the user), and ensuring that matches are exhaustive (which is important for soundness, because a match expression can return a value). @@ -88,22 +87,26 @@ That file contains a detailed description of the algorithm. ### Constructors and fields In the value `Pair(Some(0), true)`, `Pair` is called the constructor of the value, and `Some(0)` and -`true` are its fields. Every matchable value can be decomposed in this way. Examples of -constructors are: `Some`, `None`, `(,)` (the 2-tuple constructor), `Foo {..}` (the constructor for -a struct `Foo`), and `2` (the constructor for the number `2`). - -Each constructor takes a fixed number of fields; this is called its arity. `Pair` and `(,)` have -arity 2, `Some` has arity 1, `None` and `42` have arity 0. Each type has a known set of -constructors. Some types have many constructors (like `u64`) or even an infinitely many (like `&str` -and `&[T]`). - -Patterns are similar: `Pair(Some(_), _)` has constructor `Pair` and two fields. The difference is -that we get some extra pattern-only constructors, namely: the wildcard `_`, variable bindings, -integer ranges like `0..=10`, and variable-length slices like `[_, .., _]`. We treat or-patterns -separately. +`true` are its fields. +Every matchable value can be decomposed in this way. +Examples of constructors are: +`Some`, `None`, `(,)` (the 2-tuple constructor), `Foo {..}` (the constructor for a struct `Foo`), +and `2` (the constructor for the number `2`). + +Each constructor takes a fixed number of fields; this is called its arity. +`Pair` and `(,)` have arity 2, `Some` has arity 1, `None` and `42` have arity 0. +Each type has a known set of constructors. +Some types have many constructors (like `u64`) or even an infinitely many (like `&str` and `&[T]`). + +Patterns are similar: `Pair(Some(_), _)` has constructor `Pair` and two fields. +The difference is that we get some extra pattern-only constructors, namely: +the wildcard `_`, variable bindings, +integer ranges like `0..=10`, and variable-length slices like `[_, .., _]`. +We treat or-patterns separately. Now to check if a value `v` matches a pattern `p`, we check if `v`'s constructor matches `p`'s -constructor, then recursively compare their fields if necessary. A few representative examples: +constructor, then recursively compare their fields if necessary. +A few representative examples: - `matches!(v, _) := true` - `matches!((v0, v1), (p0, p1)) := matches!(v0, p0) && matches!(v1, p1)` @@ -114,8 +117,9 @@ constructor, then recursively compare their fields if necessary. A few represent - `matches!([v0], [p0, .., p1]) := false` (incompatible lengths) - `matches!([v0, v1, v2], [p0, .., p1]) := matches!(v0, p0) && matches!(v2, p1)` -This concept is absolutely central to pattern analysis. The [`constructor`] module provides -functions to extract, list and manipulate constructors. This is a useful enough concept that +This concept is absolutely central to pattern analysis. +The [`constructor`] module provides functions to extract, list, and manipulate constructors. +This is a useful enough concept that variations of it can be found in other places of the compiler, like in the MIR-lowering of a match expression and in some clippy lints. @@ -125,7 +129,8 @@ The pattern-only constructors (`_`, ranges and variable-length slices) each stan normal constructors, e.g. `_: Option` stands for the set {`None`, `Some`} and `[_, .., _]` stands for the infinite set {`[,]`, `[,,]`, `[,,,]`, ...} of the slice constructors of arity >= 2. -In order to manage these constructors, we keep them as grouped as possible. For example: +In order to manage these constructors, we keep them as grouped as possible. +For example: ```rust match (0, false) { @@ -137,7 +142,8 @@ match (0, false) { In this example, all of `0`, `1`, .., `49` match the same arms, and thus can be treated as a group. In fact, in this match, the only ranges we need to consider are: `0..50`, `50..=100`, -`101..=150`,`151..=200` and `201..`. Similarly: +`101..=150`,`151..=200` and `201..`. +Similarly: ```rust enum Direction { North, South, East, West } @@ -156,10 +162,11 @@ time. ### Usefulness vs reachability in the presence of empty types -This is likely the subtlest aspect of exhaustiveness. To be fully precise, a match doesn't operate -on a value, it operates on a place. In certain unsafe circumstances, it is possible for a place to -not contain valid data for its type. This has subtle consequences for empty types. Take the -following: +This is likely the subtlest aspect of exhaustiveness. +To be fully precise, a match doesn't operate on a value; it operates on a place. +In certain unsafe circumstances, it is possible for a place to not contain valid data for its type. +This has subtle consequences for empty types. +Take the following: ```rust enum Void {} @@ -172,10 +179,11 @@ unsafe { } ``` -In this example, `ptr` is a valid pointer pointing to a place with invalid data. The `_` pattern -does not look at the contents of the place `*ptr`, so this code is ok and the arm is taken. In other -words, despite the place we are inspecting being of type `Void`, there is a reachable arm. If the -arm had a binding however: +In this example, `ptr` is a valid pointer pointing to a place with invalid data. +The `_` pattern does not look at the contents of the place `*ptr`, +so this code is ok and the arm is taken. +In other words, despite the place we are inspecting being of type `Void`, there is a reachable arm. +If the arm had a binding however: ```rust # #[derive(Copy, Clone)] @@ -189,25 +197,31 @@ match *ptr { # } ``` -Here the binding loads the value of type `Void` from the `*ptr` place. In this example, this causes -UB since the data is not valid. In the general case, this asserts validity of the data at `*ptr`. +Here the binding loads the value of type `Void` from the `*ptr` place. +In this example, this causes UB since the data is not valid. +In the general case, this asserts validity of the data at `*ptr`. Either way, this arm will never be taken. -Finally, let's consider the empty match `match *ptr {}`. If we consider this exhaustive, then -having invalid data at `*ptr` is invalid. In other words, the empty match is semantically -equivalent to the `_a => ...` match. In the interest of explicitness, we prefer the case with an -arm, hence we won't tell the user to remove the `_a` arm. In other words, the `_a` arm is -unreachable yet not redundant. This is why we lint on redundant arms rather than unreachable +Finally, let's consider the empty match `match *ptr {}`. +If we consider this exhaustive, then having invalid data at `*ptr` is invalid. +In other words, the empty match is semantically equivalent to the `_a => ...` match. +In the interest of explicitness, we prefer the case with an +arm, hence we won't tell the user to remove the `_a` arm. +In other words, the `_a` arm is unreachable yet not redundant. +This is why we lint on redundant arms rather than unreachable arms, despite the fact that the lint says "unreachable". These considerations only affects certain places, namely those that can contain non-valid data -without UB. These are: pointer dereferences, reference dereferences, and union field accesses. We -track during exhaustiveness checking whether a given place is known to contain valid data. +without UB. +These are: pointer dereferences, reference dereferences, and union field accesses. +We track during exhaustiveness checking whether a given place is known to contain valid data. Having said all that, the current implementation of exhaustiveness checking does not follow the -above considerations. On stable, empty types are for the most part treated as non-empty. The -[`exhaustive_patterns`] feature errs on the other end: it allows omitting arms that could be -reachable in unsafe situations. The [`never_patterns`] experimental feature aims to fix this and +above considerations. +On stable, empty types are for the most part treated as non-empty. +The [`exhaustive_patterns`] feature errs on the other end: it allows omitting arms that could be +reachable in unsafe situations. +The [`never_patterns`] experimental feature aims to fix this and permit the correct behavior of empty types in patterns. [`check_match`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/thir/pattern/check_match/index.html diff --git a/src/doc/rustc-dev-guide/src/profiling/with-perf.md b/src/doc/rustc-dev-guide/src/profiling/with-perf.md index 3285a486eb452..becaec831230c 100644 --- a/src/doc/rustc-dev-guide/src/profiling/with-perf.md +++ b/src/doc/rustc-dev-guide/src/profiling/with-perf.md @@ -17,10 +17,9 @@ This is a guide for how to profile rustc with [perf](https://perf.wiki.kernel.or ## Gathering a perf profile -perf is an excellent tool on linux that can be used to gather and -analyze all kinds of information. Mostly it is used to figure out -where a program spends its time. It can also be used for other sorts -of events, though, like cache misses and so forth. +perf is an excellent tool on Linux that can be used to gather and analyze all kinds of information. +It is mostly used to figure out where a program spends its time. +It can also be used for other sorts of events, though, like cache misses and so forth. ### The basics @@ -30,26 +29,26 @@ The basic `perf` command is this: perf record -F99 --call-graph dwarf XXX ``` -The `-F99` tells perf to sample at 99 Hz, which avoids generating too -much data for longer runs (why 99 Hz you ask? It is often chosen -because it is unlikely to be in lockstep with other periodic -activity). The `--call-graph dwarf` tells perf to get call-graph -information from debuginfo, which is accurate. The `XXX` is the -command you want to profile. So, for example, you might do: +The `-F99` tells perf to sample at 99 Hz, which avoids generating too much data for longer runs. + +> 99 Hz is often chosen because it is unlikely to be in lockstep with other periodic activity. + +The `--call-graph dwarf` tells perf to get call-graph information from debuginfo, which is accurate. +The `XXX` is the command you want to profile. +So, for example, you might do: ```bash perf record -F99 --call-graph dwarf cargo + rustc ``` -to run `cargo` -- here `` should be the name of the toolchain -you made in the beginning. But there are some things to be aware of: +to run `cargo` -- here `` should be the name of the toolchain you made in the beginning. +But there are some things to be aware of: -- You probably don't want to profile the time spend building - dependencies. So something like `cargo build; cargo clean -p $C` may - be helpful (where `$C` is the crate name) - - Though usually I just do `touch src/lib.rs` and rebuild instead. =) -- You probably don't want incremental messing about with your - profile. So something like `CARGO_INCREMENTAL=0` can be helpful. +- You probably don't want to profile the time spent building dependencies. + So something like `cargo build; cargo clean --package $crate` may be helpful + - Though usually I just do `touch src/lib.rs` and rebuild instead =) +- You probably don't want incremental messing about with your profile. + So something like `CARGO_INCREMENTAL=0` can be helpful. In case to avoid the issue of `addr2line xxx/elf: could not read first record` when reading collected data from `cargo`, you may need use the latest version of `addr2line`: @@ -61,20 +60,20 @@ cargo install addr2line --features="bin" ### Gathering a perf profile from a `perf.rust-lang.org` test Often we want to analyze a specific test from `perf.rust-lang.org`. -The easiest way to do that is to use the [rustc-perf][rustc-perf] +The easiest way to do that is to use the [rustc-perf] benchmarking suite, this approach is described [here](with-rustc-perf.md). -Instead of using the benchmark suite CLI, you can also profile the benchmarks manually. First, -you need to clone the [rustc-perf][rustc-perf] repository: +Instead of using the benchmark suite CLI, you can also profile the benchmarks manually. +First, you need to clone the [rustc-perf] repository: ```bash $ git clone https://github.com/rust-lang/rustc-perf ``` -and then find the source code of the test that you want to profile. Sources for the tests -are found in [the `collector/compile-benchmarks` directory][compile-time dir] -and [the `collector/runtime-benchmarks` directory][runtime dir]. So let's -go into the directory of a specific test; we'll use `clap-rs` as an example: +and then find the source code of the test that you want to profile. +Sources for the tests are found in [the `collector/compile-benchmarks` directory][compile-time dir] +and [the `collector/runtime-benchmarks` directory][runtime dir]. +So let's go into the directory of a specific test; we'll use `clap-rs` as an example: [rustc-perf]: https://github.com/rust-lang/rustc-perf [compile-time dir]: https://github.com/rust-lang/rustc-perf/tree/master/collector/compile-benchmarks @@ -84,9 +83,8 @@ go into the directory of a specific test; we'll use `clap-rs` as an example: cd collector/compile-benchmarks/clap-3.1.6 ``` -In this case, let's say we want to profile the `cargo check` -performance. In that case, I would first run some basic commands to -build the dependencies: +In this case, let's say we want to profile the `cargo check` performance. +In that case, I would first run some basic commands to build the dependencies: ```bash # Setup: first clean out any old results and build the dependencies: @@ -94,11 +92,11 @@ cargo + clean CARGO_INCREMENTAL=0 cargo + check ``` -(Again, `` should be replaced with the name of the -toolchain we made in the first step.) +(Again, `` should be replaced with the name of the toolchain we made in the first step.) Next: we want record the execution time for *just* the clap-rs crate, -running cargo check. I tend to use `cargo rustc` for this, since it +running cargo check. +I tend to use `cargo rustc` for this, since it also allows me to add explicit flags, which we'll do later on. ```bash @@ -106,26 +104,26 @@ touch src/lib.rs CARGO_INCREMENTAL=0 perf record -F99 --call-graph dwarf cargo rustc --profile check --lib ``` -Note that final command: it's a doozy! It uses the `cargo rustc` -command, which executes rustc with (potentially) additional options; +Note that final command: it's a doozy! +It uses the `cargo rustc` command, which executes rustc with (potentially) additional options; the `--profile check` and `--lib` options specify that we are doing a `cargo check` execution, and that this is a library (not a binary). -At this point, we can use `perf` tooling to analyze the results. For example: +At this point, we can use `perf` tooling to analyze the results. +For example: ```bash perf report ``` -will open up an interactive TUI program. In simple cases, that can be -helpful. For more detailed examination, the [`perf-focus` tool][pf] -can be helpful; it is covered below. +This opens up an interactive TUI program. +In simple cases, that can be helpful. +For more detailed examination, the [`perf-focus` tool][pf] can be helpful; it is covered below. -**A note of caution.** Each of the rustc-perf tests is its own special - snowflake. In particular, some of them are not libraries, in which - case you would want to do `touch src/main.rs` and avoid passing - `--lib`. I'm not sure how best to tell which test is which to be - honest. +**A note of caution.** Each of the rustc-perf tests is its own special snowflake. + In particular, some of them are not libraries, in which + case you would want to do `touch src/main.rs` and avoid passing `--lib`. + I'm not sure how best to tell which test is which to be honest. ### Gathering NLL data @@ -141,23 +139,22 @@ CARGO_INCREMENTAL=0 perf record -F99 --call-graph dwarf cargo rustc --profile ch ## Analyzing a perf profile with `perf focus` -Once you've gathered a perf profile, we want to get some information -about it. For this, I personally use [perf focus][pf]. It's a kind of -simple but useful tool that lets you answer queries like: +Once you've gathered a perf profile, we want to get some information about it. +For this, I personally use [perf focus][pf]. +It's a kind of simple but useful tool that lets you answer queries like: - "how much time was spent in function F" (no matter where it was called from) - "how much time was spent in function F when it was called from G" - "how much time was spent in function F *excluding* time spent in G" - "what functions does F call and how much time does it spend in them" -To understand how it works, you have to know just a bit about -perf. Basically, perf works by *sampling* your process on a regular -basis (or whenever some event occurs). For each sample, perf gathers a -backtrace. `perf focus` lets you write a regular expression that tests +To understand how it works, you have to know just a bit about perf. +Basically, perf works by *sampling* your process on a regular basis (or whenever some event occurs). +For each sample, perf gathers a backtrace. +`perf focus` lets you write a regular expression that tests which functions appear in that backtrace, and then tells you which -percentage of samples had a backtrace that met the regular -expression. It's probably easiest to explain by walking through how I -would analyze NLL performance. +percentage of samples had a backtrace that met the regular expression. +It's probably easiest to explain by walking through how I would analyze NLL performance. ### Installing `perf-focus` @@ -169,9 +166,9 @@ cargo install perf-focus ### Example: How much time is spent in MIR borrowck? -Let's say we've gathered the NLL data for a test. We'd like to know -how much time it is spending in the MIR borrow-checker. The "main" -function of the MIR borrowck is called `do_mir_borrowck`, so we can do +Let's say we've gathered the NLL data for a test. +We'd like to know how much time it is spending in the MIR borrow-checker. +The "main" function of the MIR borrowck is called `do_mir_borrowck`, so we can do this command: ```bash @@ -183,37 +180,38 @@ Percentage : 29% ``` The `'{do_mir_borrowck}'` argument is called the **matcher**. It -specifies the test to be applied on the backtrace. In this case, the -`{X}` indicates that there must be *some* function on the backtrace -that meets the regular expression `X`. In this case, that regex is -just the name of the function we want (in fact, it's a subset of the name; -the full name includes a bunch of other stuff, like the module -path). In this mode, perf-focus just prints out the percentage of +specifies the test to be applied on the backtrace. +In this case, the `{X}` indicates that there must be *some* function on the backtrace +that meets the regular expression `X`. +In this case, that regex is just the name of the function we want +(in fact, it's a subset of the name; +the full name includes a bunch of other stuff, like the module path). +In this mode, perf-focus just prints out the percentage of samples where `do_mir_borrowck` was on the stack: in this case, 29%. **A note about c++filt.** To get the data from `perf`, `perf focus` - currently executes `perf script` (perhaps there is a better - way...). I've sometimes found that `perf script` outputs C++ mangled + currently executes `perf script` (perhaps there is a better way...). + I've sometimes found that `perf script` outputs C++ mangled names. This is annoying. You can tell by running `perf script | head` yourself — if you see names like `5rustc6middle` instead of - `rustc::middle`, then you have the same problem. You can solve this - by doing: + `rustc::middle`, then you have the same problem. + You can solve this by doing: ```bash perf script | c++filt | perf focus --from-stdin ... ``` This will pipe the output from `perf script` through `c++filt` and -should mostly convert those names into a more friendly format. The -`--from-stdin` flag to `perf focus` tells it to get its data from -stdin, rather than executing `perf focus`. We should make this more -convenient (at worst, maybe add a `c++filt` option to `perf focus`, or +should mostly convert those names into a more friendly format. +The `--from-stdin` flag to `perf focus` tells it to get its data from +stdin, rather than executing `perf focus`. +We should make this more convenient (at worst, maybe add a `c++filt` option to `perf focus`, or just always use it — it's pretty harmless). ### Example: How much time does MIR borrowck spend solving traits? -Perhaps we'd like to know how much time MIR borrowck spends in the -trait checker. We can ask this using a more complex regex: +Perhaps we'd like to know how much time MIR borrowck spends in the trait checker. +We can ask this using a more complex regex: ```bash $ perf focus '{do_mir_borrowck}..{^rustc::traits}' @@ -225,23 +223,21 @@ Percentage : 0% Here we used the `..` operator to ask "how often do we have `do_mir_borrowck` on the stack and then, later, some function whose -name begins with `rustc::traits`?" (basically, code in that module). It -turns out the answer is "almost never" — only 12 samples fit that -description (if you ever see *no* samples, that often indicates your -query is messed up). +name begins with `rustc::traits`?" (basically, code in that module). +It turns out the answer is "almost never" — only 12 samples fit that +description (if you ever see *no* samples, that often indicates your query is messed up). -If you're curious, you can find out exactly which samples by using the -`--print-match` option. This will print out the full backtrace for -each sample. The `|` at the front of the line indicates the part that -the regular expression matched. +If you're curious, you can find out exactly which samples by using the `--print-match` option. +This will print out the full backtrace for each sample. +The `|` at the front of the line indicates the part that the regular expression matched. ### Example: Where does MIR borrowck spend its time? -Often we want to do more "explorational" queries. Like, we know that -MIR borrowck is 29% of the time, but where does that time get spent? -For that, the `--tree-callees` option is often the best tool. You -usually also want to give `--tree-min-percent` or -`--tree-max-depth`. The result looks like this: +Often we want to do more "explorational" queries. +Like, we know that MIR borrowck is 29% of the time, but where does that time get spent? +For that, the `--tree-callees` option is often the best tool. +You usually also want to give `--tree-min-percent` or `--tree-max-depth`. +The result looks like this: ```bash $ perf focus '{do_mir_borrowck}' --tree-callees --tree-min-percent 3 @@ -265,33 +261,30 @@ Tree What happens with `--tree-callees` is that - we find each sample matching the regular expression -- we look at the code that occurs *after* the regex match and try - to build up a call tree +- we look at the code that occurs *after* the regex match and try to build up a call tree -The `--tree-min-percent 3` option says "only show me things that take -more than 3% of the time". Without this, the tree often gets really -noisy and includes random stuff like the innards of -malloc. `--tree-max-depth` can be useful too, it just limits how many -levels we print. +The `--tree-min-percent 3` option says "only show me things that take more than 3% of the time". +Without this, +the tree often gets really noisy and includes random stuff like the innards of malloc. +`--tree-max-depth` can be useful too, it just limits how many levels we print. For each line, we display the percent of time in that function altogether ("total") and the percent of time spent in **just that -function and not some callee of that function** (self). Usually -"total" is the more interesting number, but not always. +function and not some callee of that function** (self). +Usually "total" is the more interesting number, but not always. ### Relative percentages By default, all in perf-focus are relative to the **total program execution**. This is useful to help you keep perspective — often as we drill down to find hot spots, we can lose sight of the fact that, -in terms of overall program execution, this "hot spot" is actually not -important. It also ensures that percentages between different queries -are easily compared against one another. +in terms of overall program execution, this "hot spot" is actually not important. +It also ensures that percentages between different queries are easily compared against one another. That said, sometimes it's useful to get relative percentages, so `perf -focus` offers a `--relative` option. In this case, the percentages are -listed only for samples that match (vs all samples). So for example we -could get our percentages relative to the borrowck itself +focus` offers a `--relative` option. +In this case, the percentages are listed only for samples that match (vs all samples). +So for example we could get our percentages relative to the borrowck itself like so: ```bash @@ -310,6 +303,7 @@ Tree ``` Here you see that `compute_regions` came up as "47% total" — that -means that 47% of `do_mir_borrowck` is spent in that function. Before, +means that 47% of `do_mir_borrowck` is spent in that function. +Before, we saw 20% — that's because `do_mir_borrowck` itself is only 43% of the total time (and `.47 * .43 = .20`). diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md index d18021bf278d9..951a541548b23 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md @@ -5,7 +5,9 @@ This page is about the test suite named `rustdoc-gui` used to test the "GUI" of `rustdoc` (i.e., the HTML/JS/CSS as rendered in a browser). For other rustdoc-specific test suites, see [Rustdoc test suites]. -These use a NodeJS-based tool called [`browser-UI-test`] that uses [puppeteer] to run tests in a headless browser and check rendering and interactivity. For information on how to write this form of test, see [`tests/rustdoc-gui/README.md`][rustdoc-gui-readme] as well as [the description of the `.goml` format][goml-script] +These use a NodeJS-based tool called [`browser-UI-test`] that uses [puppeteer] to run tests in a headless browser and check rendering and interactivity. +For information on how to write this form of test, +see [`tests/rustdoc-gui/README.md`][rustdoc-gui-readme] as well as [the description of the `.goml` format][goml-script]. [Rustdoc test suites]: ../tests/compiletest.md#rustdoc-test-suites [`browser-UI-test`]: https://github.com/GuillaumeGomez/browser-UI-test/ diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md index e88d2703e3a23..ac2b4da5c652e 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md @@ -1,16 +1,13 @@ # Rustdoc search -Rustdoc Search is two programs: `search_index.rs` -and `search.js`. The first generates a nasty JSON -file with a full list of items and function signatures +Rustdoc Search is two programs: `search_index.rs` and `search.js`. +The first generates a nasty JSON file with a full list of items and function signatures in the crates in the doc bundle, and the second reads -it, turns it into some in-memory structures, and -scans them linearly to search. +it, turns it into some in-memory structures, and scans them linearly to search. ## Search index format -`search.js` calls this Raw, because it turns it into -a more normal object tree after loading it. +`search.js` calls this Raw, because it turns it into a more normal object tree after loading it. For space savings, it's also written without newlines or spaces. ```json @@ -44,8 +41,7 @@ For space savings, it's also written without newlines or spaces. ] ``` -[`src/librustdoc/html/static/js/rustdoc.d.ts`] -defines an actual schema in a TypeScript `type`. +[`src/librustdoc/html/static/js/rustdoc.d.ts`] defines an actual schema in a TypeScript `type`. | Key | Name | Description | | --- | -------------------- | ------------ | @@ -75,10 +71,8 @@ It makes a lot of compromises: * The `rustdoc` compiler runs on one crate at a time, so each crate has an essentially separate search index. - It [merges] them by having each crate on one line - and looking at the first quoted string. -* Names in the search index are given - in their original case and with underscores. + It [merges] them by having each crate on one line and looking at the first quoted string. +* Names in the search index are given in their original case and with underscores. When the search index is loaded, `search.js` stores the original names for display, but also folds them to lowercase and strips underscores for search. @@ -156,10 +150,8 @@ for (i, entry) in search_index.iter().enumerate() { } ``` -This is valid because everything has a parent module -(even if it's just the crate itself), -and is easy to assemble because the rustdoc generator sorts by path -before serializing. +This is valid because everything has a parent module (even if it's just the crate itself), +and is easy to assemble because the rustdoc generator sorts by path before serializing. Doing this allows rustdoc to not only make the search index smaller, but reuse the same string representing the parent path across multiple in-memory items. @@ -233,21 +225,21 @@ work through a "sandwich workload" of three steps: Reducing the amount of data downloaded here will almost always increase latency, by delaying the decision of what to download behind other work and/or adding -data dependencies where something can't be downloaded without first downloading -something else. In this case, we can't start downloading descriptions until +data dependencies where something can't be downloaded without first downloading something else. +In this case, we can't start downloading descriptions until after the search is done, because that's what allows it to decide *which* descriptions to download (it needs to sort the results then truncate to 200). To do this, two columns are stored in the search index, building on both Roaring Bitmaps and on VLQ Hex. -* `e` is an index of **e**mpty descriptions. It's a [roaring bitmap] of - each item (the crate itself is item 0, the rest start at 1). +* `e` is an index of **e**mpty descriptions. + It's a [roaring bitmap] of each item (the crate itself is item 0, the rest start at 1). * `D` is a shard list, stored in [VLQ hex] as flat list of integers. Each integer gives you the number of descriptions in the shard. As the decoder walks the index, it checks if the description is empty. - if it's not, then it's in the "current" shard. When all items are - exhausted, it goes on to the next shard. + if it's not, then it's in the "current" shard. + When all items are exhausted, it goes on to the next shard. Inside each shard is a newline-delimited list of descriptions, wrapped in a JSONP-style function call. @@ -280,8 +272,7 @@ Because of zigzag encoding, `` ` `` is +0, `a` is -0 (which is not used), ## Searching by name -Searching by name works by looping through the search index -and running these functions on each: +Searching by name works by looping through the search index and running these functions on each: * [`editDistance`] is always used to determine a match (unless quotes are specified, which would use simple equality instead). @@ -295,20 +286,19 @@ and running these functions on each: If it returns anything other than -1, the result is added, even if `editDistance` exceeds its threshold, and the index is stored for ranking. -* [`checkPath`] is used if, and only if, a parent path is specified - in the query. For example, `vec` has no parent path, but `vec::vec` does. +* [`checkPath`] is used if, and only if, a parent path is specified in the query. + For example, `vec` has no parent path, but `vec::vec` does. Within checkPath, editDistance and indexOf are used, and the path query has its own heuristic threshold, too. If it's not within the threshold, the entry is rejected, even if the first two pass. - If it's within the threshold, the path distance is stored - for ranking. + If it's within the threshold, the path distance is stored for ranking. * [`checkType`] is used only if there's a type filter, - like the struct in `struct:vec`. If it fails, + like the struct in `struct:vec`. + If it fails, the entry is rejected. -If all four criteria pass -(plus the crate filter, which isn't technically part of the query), +If all four criteria pass (plus the crate filter, which isn't technically part of the query), the results are sorted by [`sortResults`]. [`editDistance`]: https://github.com/rust-lang/rust/blob/79b710c13968a1a48d94431d024d2b1677940866/src/librustdoc/html/static/js/search.js#L137 @@ -336,17 +326,18 @@ is going to match the same things `T, u32` matches (though rustdoc will detect this particular problem and warn about it). Then, when actually looping over each item, -the bloom filter will probably reject entries that don't have every -type mentioned in the query. +the bloom filter will probably reject entries that don't have every type mentioned in the query. For example, the bloom query allows a query of `i32 -> u32` to match a function with the type `i32, u32 -> bool`, but unification will reject it later. The unification filter ensures that: -* Bag semantics are respected. If you query says `i32, i32`, +* Bag semantics are respected. + If your query says `i32, i32`, then the function has to mention *two* i32s, not just one. -* Nesting semantics are respected. If your query says `vec