From d8ecbe7c01148d198e51ce72a202255144fd4fc7 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sat, 8 Aug 2026 12:13:25 +0200 Subject: [PATCH 1/6] Move `ExprParenthesesNeeded` diagnostic struct out of `rustc_session` It was never used in that crate, so rustc_parse is the next obvious place to go. It's also used by rustc_hir_typeck, but sharing diagnostics between crates makes it easy for such things to become dead, so duplicate it. --- compiler/rustc_hir_typeck/src/diagnostics.rs | 18 ++++++++++++++++++ compiler/rustc_hir_typeck/src/expr.rs | 10 +++++----- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 3 +-- .../src/fn_ctxt/suggestions.rs | 3 +-- compiler/rustc_hir_typeck/src/op.rs | 2 +- compiler/rustc_parse/src/diagnostics.rs | 19 ++++++++++++++++++- .../rustc_parse/src/parser/diagnostics.rs | 3 +-- compiler/rustc_parse/src/parser/expr.rs | 3 ++- compiler/rustc_parse/src/parser/pat.rs | 13 ++++++------- compiler/rustc_session/src/diagnostics.rs | 18 ------------------ 10 files changed, 53 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/diagnostics.rs b/compiler/rustc_hir_typeck/src/diagnostics.rs index 1a6df92957d00..722dfb0794ec1 100644 --- a/compiler/rustc_hir_typeck/src/diagnostics.rs +++ b/compiler/rustc_hir_typeck/src/diagnostics.rs @@ -1327,3 +1327,21 @@ pub(crate) struct FloatLiteralF32Fallback { )] pub span: Option, } + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "parentheses are required to parse this as an expression", + applicability = "machine-applicable" +)] +pub(crate) struct ExprParenthesesNeeded { + #[suggestion_part(code = "(")] + left: Span, + #[suggestion_part(code = ")")] + right: Span, +} + +impl ExprParenthesesNeeded { + pub(crate) fn surrounding(s: Span) -> Self { + ExprParenthesesNeeded { left: s.shrink_to_lo(), right: s.shrink_to_hi() } + } +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index e2aef3c0bdb8c..12fbcca492a29 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -31,7 +31,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, AdtKind, GenericArgsRef, Ty, TypeVisitableExt, Unnormalized}; use rustc_middle::{bug, span_bug}; -use rustc_session::diagnostics::{ExprParenthesesNeeded, feature_err}; +use rustc_session::diagnostics::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; use rustc_span::{Ident, Span, Spanned, Symbol, kw, sym}; @@ -43,10 +43,10 @@ use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectatio use crate::coercion::CoerceMany; use crate::diagnostics::{ AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr, - BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer, - FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, - NoFieldOnVariant, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, - TypeMismatchFruTypo, YieldExprOutsideOfCoroutine, + BaseExpressionDoubleDotRemove, CantDereference, ExprParenthesesNeeded, + FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, + NakedAsmOutsideNakedFn, NoFieldOnVariant, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, + StructExprNonExhaustive, TypeMismatchFruTypo, YieldExprOutsideOfCoroutine, }; use crate::op::contains_let_in_chain; use crate::{ diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index f1c98c16651cc..2b92eb7e8f83e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -23,7 +23,6 @@ use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; use rustc_middle::{bug, span_bug}; use rustc_session::Session; -use rustc_session::diagnostics::ExprParenthesesNeeded; use rustc_span::{DUMMY_SP, Ident, Span, kw, sym}; use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt}; use rustc_trait_selection::infer::InferCtxtExt; @@ -34,7 +33,7 @@ use tracing::debug; use crate::Expectation::*; use crate::TupleArgumentsFlag::*; use crate::coercion::CoerceMany; -use crate::diagnostics::SuggestPtrNullMut; +use crate::diagnostics::{ExprParenthesesNeeded, SuggestPtrNullMut}; use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx}; use crate::gather_locals::Declaration; use crate::inline_asm::InlineAsmCtxt; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index a09da3cec2f92..108f3f99df202 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -24,7 +24,6 @@ use rustc_middle::ty::{ self, Article, Binder, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, Unnormalized, Upcast, suggest_constraining_type_params, }; -use rustc_session::diagnostics::ExprParenthesesNeeded; use rustc_span::{ExpnKind, Ident, MacroKind, Span, Spanned, Symbol, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::DefIdOrName; @@ -35,7 +34,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _ use tracing::{debug, instrument}; use super::FnCtxt; -use crate::diagnostics::{self, SuggestBoxingForReturnImplTrait}; +use crate::diagnostics::{self, ExprParenthesesNeeded, SuggestBoxingForReturnImplTrait}; use crate::fn_ctxt::rustc_span::BytePos; use crate::method::probe; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 99833a5f81cb9..c28976555432b 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -14,7 +14,6 @@ use rustc_middle::ty::adjustment::{ }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; -use rustc_session::diagnostics::ExprParenthesesNeeded; use rustc_span::{Span, Spanned, Symbol, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt}; @@ -22,6 +21,7 @@ use tracing::debug; use super::FnCtxt; use super::method::MethodCallee; +use crate::diagnostics::ExprParenthesesNeeded; use crate::method::TreatNotYetDefinedOpaques; use crate::{Expectation, diagnostics}; diff --git a/compiler/rustc_parse/src/diagnostics.rs b/compiler/rustc_parse/src/diagnostics.rs index 371604b88ef2b..8a41b5b15fb5c 100644 --- a/compiler/rustc_parse/src/diagnostics.rs +++ b/compiler/rustc_parse/src/diagnostics.rs @@ -11,7 +11,6 @@ use rustc_errors::{ Level, Subdiagnostic, SuggestionStyle, msg, }; use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_session::diagnostics::ExprParenthesesNeeded; use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; use rustc_span::{Ident, Span, Symbol}; @@ -923,6 +922,24 @@ pub(crate) struct FoundExprWouldBeStmt { pub suggestion: ExprParenthesesNeeded, } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "parentheses are required to parse this as an expression", + applicability = "machine-applicable" +)] +pub(crate) struct ExprParenthesesNeeded { + #[suggestion_part(code = "(")] + left: Span, + #[suggestion_part(code = ")")] + right: Span, +} + +impl ExprParenthesesNeeded { + pub(crate) fn surrounding(s: Span) -> Self { + ExprParenthesesNeeded { left: s.shrink_to_lo(), right: s.shrink_to_hi() } + } +} + #[derive(Diagnostic)] #[diag("extra characters after frontmatter close are not allowed")] pub(crate) struct FrontmatterExtraCharactersAfterClose { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 6176784b3bbe3..a3c6e6262b4d3 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -15,7 +15,6 @@ use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, PResult, Subdiagnostic, Suggestions, msg, pluralize, }; -use rustc_session::diagnostics::ExprParenthesesNeeded; use rustc_span::symbol::used_keywords; use rustc_span::{BytePos, DUMMY_SP, Ident, Span, SpanSnippetError, Spanned, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; @@ -31,7 +30,7 @@ use crate::diagnostics::{ AwaitSuggestion, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi, ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg, DocCommentDoesNotDocumentAnything, DocCommentOnParamType, DoubleColonInBound, - ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, FoundPathInGenerics, + ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, ExprParenthesesNeeded, FoundPathInGenerics, GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, MisspelledKw, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 34044e72ab92b..9f1dd349e668d 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -21,7 +21,7 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic}; use rustc_literal_escaper::unescape_char; -use rustc_session::diagnostics::{ExprParenthesesNeeded, report_lit_error}; +use rustc_session::diagnostics::report_lit_error; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_span::edition::Edition; use rustc_span::{BytePos, ErrorGuaranteed, Ident, Pos, Span, Spanned, Symbol, kw, respan, sym}; @@ -35,6 +35,7 @@ use super::{ AttrWrapper, BlockMode, ClosureSpans, ExpTokenPair, ForceCollect, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenType, Trailing, UsePreAttrPos, }; +use crate::diagnostics::ExprParenthesesNeeded; use crate::{diagnostics, exp, maybe_recover_from_interpolated_ty_qpath}; #[derive(Debug)] diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 74b2194cc97fa..2abea006544b2 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -12,7 +12,6 @@ use rustc_ast::{ }; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey}; -use rustc_session::diagnostics::ExprParenthesesNeeded; use rustc_span::{BytePos, ErrorGuaranteed, Ident, Span, Spanned, kw, respan, sym}; use thin_vec::{ThinVec, thin_vec}; @@ -21,12 +20,12 @@ use crate::diagnostics::{ self, AmbiguousRangePattern, AtDotDotInStructPattern, AtInStructPattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, ExpectedCommaAfterPatternField, - GenericArgsInPatRequireTurbofishSyntax, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, - InclusiveRangeNoEnd, InvalidMutInPattern, ParenRangeSuggestion, PatternOnWrongSideOfAt, - RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, - TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, TrailingVertSuggestion, - UnexpectedExpressionInPattern, UnexpectedExpressionInPatternSugg, UnexpectedLifetimeInPattern, - UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, + ExprParenthesesNeeded, GenericArgsInPatRequireTurbofishSyntax, InclusiveRangeExtraEquals, + InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, ParenRangeSuggestion, + PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, + TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, + TrailingVertSuggestion, UnexpectedExpressionInPattern, UnexpectedExpressionInPatternSugg, + UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens, }; use crate::parser::expr::{DestructuredFloat, could_be_unclosed_char_literal}; diff --git a/compiler/rustc_session/src/diagnostics.rs b/compiler/rustc_session/src/diagnostics.rs index 9efc4bc4a1df8..c229adf5aef4d 100644 --- a/compiler/rustc_session/src/diagnostics.rs +++ b/compiler/rustc_session/src/diagnostics.rs @@ -451,24 +451,6 @@ pub(crate) struct InvalidCharacterInCrateNameSuggestion { pub(crate) suggested_name: String, } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "parentheses are required to parse this as an expression", - applicability = "machine-applicable" -)] -pub struct ExprParenthesesNeeded { - #[suggestion_part(code = "(")] - left: Span, - #[suggestion_part(code = ")")] - right: Span, -} - -impl ExprParenthesesNeeded { - pub fn surrounding(s: Span) -> Self { - ExprParenthesesNeeded { left: s.shrink_to_lo(), right: s.shrink_to_hi() } - } -} - #[derive(Diagnostic)] #[diag("skipping const checks")] pub(crate) struct SkippingConstChecks { From 4077d5c0d5e5e5d0dc870aa580ae57c32c4cfda4 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sat, 8 Aug 2026 12:23:42 +0200 Subject: [PATCH 2/6] don't pub use `NoVariantNamed` in the crate root --- compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 4 ++-- compiler/rustc_hir_analysis/src/lib.rs | 1 - compiler/rustc_hir_typeck/src/expr.rs | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 953c6a469d11c..da29861ebbe89 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -53,11 +53,11 @@ use rustc_trait_selection::traits::{self, FulfillmentError}; use tracing::{debug, instrument}; use crate::check::check_abi; -use crate::diagnostics::{self, BadReturnTypeNotation, NoFieldOnType}; +use crate::check_c_variadic_abi; +use crate::diagnostics::{self, BadReturnTypeNotation, NoFieldOnType, NoVariantNamed}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; -use crate::{NoVariantNamed, check_c_variadic_abi}; /// The context in which an implied bound is being added to a item being lowered (i.e. a sizedness /// trait or a default trait) diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index ebbf63b947a93..572200dbd7634 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -81,7 +81,6 @@ mod impl_wf_check; mod outlives; mod variance; -pub use diagnostics::NoVariantNamed; use rustc_abi::{CVariadicStatus, ExternAbi}; use rustc_hir as hir; use rustc_hir::def::DefKind; diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 12fbcca492a29..0918fdad094d0 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -22,8 +22,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal}; -use rustc_hir_analysis::NoVariantNamed; -use rustc_hir_analysis::diagnostics::NoFieldOnType; +use rustc_hir_analysis::diagnostics::{NoFieldOnType, NoVariantNamed}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, RegionVariableOrigin}; use rustc_infer::traits::query::NoSolution; From 2586cedc4affff0d23c5b8d53fbcf2c8f7e7d267 Mon Sep 17 00:00:00 2001 From: zakrad <49591476+zakrad@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:41:55 +0330 Subject: [PATCH 3/6] Add regression test for save-temps ICE on incremental recompile --- tests/incremental/save-temps-66367.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/incremental/save-temps-66367.rs diff --git a/tests/incremental/save-temps-66367.rs b/tests/incremental/save-temps-66367.rs new file mode 100644 index 0000000000000..e5568f2968bba --- /dev/null +++ b/tests/incremental/save-temps-66367.rs @@ -0,0 +1,18 @@ +//! Regression test for . +//! +//! Adding `-C save-temps` to a follow-up incremental compile used to ICE: the codegen unit was +//! copied from the incremental cache, and the copy-from-cache path asserted that no bytecode was +//! wanted. `-C save-temps` wants it, so the assertion fired. + +//@ revisions: bpass1 bpass2 +//@ compile-flags: -Z query-dep-graph --crate-type=lib +//@[bpass2] compile-flags: -C save-temps + +#![feature(rustc_attrs)] +// `-C save-temps` is not part of the incremental command line hash, so the codegen unit is still +// reused in `bpass2` -- which is the path that used to ICE. +#![rustc_partition_reused(module = "save_temps_66367", cfg = "bpass2")] + +pub fn f() -> u32 { + 1 +} From d63ff517063164fc7c0402b1cd85a2f2d73c037f Mon Sep 17 00:00:00 2001 From: zakrad <49591476+zakrad@users.noreply.github.com> Date: Sun, 9 Aug 2026 00:53:27 +0330 Subject: [PATCH 4/6] Add regression test for guaranteed unsized self type cycle --- ...cle-guaranteed-unsized-self-type-116914.rs | 27 +++++++++++++++++++ ...guaranteed-unsized-self-type-116914.stderr | 16 +++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/ui/traits/cycle-guaranteed-unsized-self-type-116914.rs create mode 100644 tests/ui/traits/cycle-guaranteed-unsized-self-type-116914.stderr diff --git a/tests/ui/traits/cycle-guaranteed-unsized-self-type-116914.rs b/tests/ui/traits/cycle-guaranteed-unsized-self-type-116914.rs new file mode 100644 index 0000000000000..43f76c7ed2021 --- /dev/null +++ b/tests/ui/traits/cycle-guaranteed-unsized-self-type-116914.rs @@ -0,0 +1,27 @@ +//! Regression test for . + +trait Filter { + type ToMatch; +} + +impl Filter for T //~ ERROR cycle detected when computing whether +where + T: Fn(Self::ToMatch), +{ +} + +trait Rule {} + +impl Rule for T +where + T: Filter, +{ +} + +struct JustFilter { + filter: F, +} + +impl Rule for JustFilter {} + +fn main() {} diff --git a/tests/ui/traits/cycle-guaranteed-unsized-self-type-116914.stderr b/tests/ui/traits/cycle-guaranteed-unsized-self-type-116914.stderr new file mode 100644 index 0000000000000..91cdd89a3babf --- /dev/null +++ b/tests/ui/traits/cycle-guaranteed-unsized-self-type-116914.stderr @@ -0,0 +1,16 @@ +error[E0391]: cycle detected when computing whether `` has a guaranteed unsized self type + --> $DIR/cycle-guaranteed-unsized-self-type-116914.rs:7:1 + | +LL | / impl Filter for T +LL | | where +LL | | T: Fn(Self::ToMatch), + | |_________________________^ + | + = note: ...which requires computing normalized predicates of ``... + = note: ...which again requires computing whether `` has a guaranteed unsized self type, completing the cycle + = note: cycle used when checking that `` is well-formed + = note: for more information, see and + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. From 82b020e8f1adb5277e355983b5ae0ace0c89215c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 9 Aug 2026 00:03:45 +0200 Subject: [PATCH 5/6] arm64ec: `f128` is supported since LLVM 23 --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index b9cde145ce514..feccbd953cc1c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -392,8 +392,7 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { cfg.has_reliable_f128 = match (target_arch, target_os) { // Unsupported https://github.com/llvm/llvm-project/issues/121122 (Arch::AmdGpu, _) => false, - // Unsupported - (Arch::Arm64EC, _) => false, + (Arch::Arm64EC, _) if major < 23 => false, // (fixed in llvm23) // Selection bug . This issue is closed // but basic math still does not work. (Arch::Nvptx64, _) => false, From 5afb1fed3eb5ec802376190d64b95884a886585e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 29 Jul 2026 13:34:33 +0300 Subject: [PATCH 6/6] Refactor tidy detection of stability attribute And fix support for multi-line attributes. And add some tests. --- src/tools/tidy/src/features.rs | 211 +++++++++++++-------------- src/tools/tidy/src/features/tests.rs | 55 +++++++ 2 files changed, 154 insertions(+), 112 deletions(-) diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index 43e6c18382af7..1b930f3249c6c 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -470,6 +470,104 @@ fn get_and_check_lib_features( lib_features } +/// `mf` gets the feature or an error if it is invalid, the file path passed as `file`, and the attribute's line number. +fn extract_lib_features<'c, 'p>( + contents: &'c str, + file: &'p Path, + mf: &mut (dyn Send + Sync + FnMut(Result<(&'c str, Feature), &'static str>, &'p Path, usize)), +) { + let handle_issue_none = |s| match s { + "none" => None, + issue => { + let n = issue.parse().expect("issue number is not a valid integer"); + assert_ne!(n, 0, "\"none\" should be used when there is no issue, not \"0\""); + NonZeroU32::new(n) + } + }; + for attr in static_regex!( + r"#!?\[\s*(?rustc_const_unstable|unstable|stable)\s*(?(\((?s).*?)\)|)\]" + ) + .captures_iter(contents) + { + let match_index = attr.get_match().start(); + let before_match = &contents[..match_index]; + let before_match_line = match before_match.rsplit_once('\n') { + Some((_, it)) => it, + None => before_match, + }; + if static_regex!(r"^\s*//").is_match(before_match_line) { + // It starts inside a comment, exclude (this does not handle block comments). + // Technically this will mis-handle things like: + // ``` + // // #[stable + // #[stable(...)] + // ``` + // Hopefully that's not a problem. + continue; + } + + let line = before_match.bytes().filter(|b| *b == b'\n').count() + 1; + + macro_rules! err { + ($msg:expr) => {{ + mf(Err($msg), file, line); + continue; + }}; + } + + let attr_meta = attr.name("attr_meta").unwrap().as_str(); + let level = match &attr["attr_name"] { + "rustc_const_unstable" => { + // `const fn` features are handled specially. + let feature_name = match find_attr_val(attr_meta, "feature") { + Some(name) => name, + None => err!("malformed stability attribute: missing `feature` key"), + }; + let feature = Feature { + level: Status::Unstable, + since: None, + has_gate_test: false, + tracking_issue: find_attr_val(attr_meta, "issue").and_then(handle_issue_none), + file: file.to_path_buf(), + line, + description: None, + }; + mf(Ok((feature_name, feature)), file, line); + continue; + } + "unstable" => Status::Unstable, + "stable" => Status::Accepted, + _ => unreachable!("unexpected attribute name"), + }; + let feature_name = match find_attr_val(attr_meta, "feature") { + Some(name) => name, + None => err!("malformed stability attribute: missing `feature` key"), + }; + let since = match find_attr_val(attr_meta, "since").map(|x| x.parse()) { + Some(Ok(since)) => Some(since), + Some(Err(_err)) => { + err!("malformed stability attribute: can't parse `since` key"); + } + None if level == Status::Accepted => { + err!("malformed stability attribute: missing the `since` key"); + } + None => None, + }; + let tracking_issue = find_attr_val(attr_meta, "issue").and_then(handle_issue_none); + + let feature = Feature { + level, + since, + has_gate_test: false, + tracking_issue, + file: file.to_path_buf(), + line, + description: None, + }; + mf(Ok((feature_name, feature)), file, line); + } +} + fn map_lib_features( base_src_path: &Path, mf: &mut (dyn Send + Sync + FnMut(Result<(&str, Feature), &str>, &Path, usize)), @@ -488,118 +586,7 @@ fn map_lib_features( return; } - // This is an early exit -- all the attributes we're concerned with must contain this: - // * rustc_const_unstable( - // * unstable( - // * stable( - if !contents.contains("stable(") { - return; - } - - let handle_issue_none = |s| match s { - "none" => None, - issue => { - let n = issue.parse().expect("issue number is not a valid integer"); - assert_ne!(n, 0, "\"none\" should be used when there is no issue, not \"0\""); - NonZeroU32::new(n) - } - }; - let mut becoming_feature: Option<(&str, Feature)> = None; - let mut iter_lines = contents.lines().enumerate().peekable(); - while let Some((i, line)) = iter_lines.next() { - macro_rules! err { - ($msg:expr) => {{ - mf(Err($msg), file, i + 1); - continue; - }}; - } - - // exclude commented out lines - if static_regex!(r"^\s*//").is_match(line) { - continue; - } - - if let Some((name, ref mut f)) = becoming_feature { - if f.tracking_issue.is_none() { - f.tracking_issue = find_attr_val(line, "issue").and_then(handle_issue_none); - } - if line.ends_with(']') { - mf(Ok((name, f.clone())), file, i + 1); - } else if !line.ends_with(',') && !line.ends_with('\\') && !line.ends_with('"') - { - // We need to bail here because we might have missed the - // end of a stability attribute above because the ']' - // might not have been at the end of the line. - // We could then get into the very unfortunate situation that - // we continue parsing the file assuming the current stability - // attribute has not ended, and ignoring possible feature - // attributes in the process. - err!("malformed stability attribute"); - } else { - continue; - } - } - becoming_feature = None; - if line.contains("rustc_const_unstable(") { - // `const fn` features are handled specially. - let feature_name = match find_attr_val(line, "feature").or_else(|| { - iter_lines.peek().and_then(|next| find_attr_val(next.1, "feature")) - }) { - Some(name) => name, - None => err!("malformed stability attribute: missing `feature` key"), - }; - let feature = Feature { - level: Status::Unstable, - since: None, - has_gate_test: false, - tracking_issue: find_attr_val(line, "issue").and_then(handle_issue_none), - file: file.to_path_buf(), - line: i + 1, - description: None, - }; - mf(Ok((feature_name, feature)), file, i + 1); - continue; - } - let level = if line.contains("[unstable(") { - Status::Unstable - } else if line.contains("[stable(") { - Status::Accepted - } else { - continue; - }; - let feature_name = match find_attr_val(line, "feature") - .or_else(|| iter_lines.peek().and_then(|next| find_attr_val(next.1, "feature"))) - { - Some(name) => name, - None => err!("malformed stability attribute: missing `feature` key"), - }; - let since = match find_attr_val(line, "since").map(|x| x.parse()) { - Some(Ok(since)) => Some(since), - Some(Err(_err)) => { - err!("malformed stability attribute: can't parse `since` key"); - } - None if level == Status::Accepted => { - err!("malformed stability attribute: missing the `since` key"); - } - None => None, - }; - let tracking_issue = find_attr_val(line, "issue").and_then(handle_issue_none); - - let feature = Feature { - level, - since, - has_gate_test: false, - tracking_issue, - file: file.to_path_buf(), - line: i + 1, - description: None, - }; - if line.contains(']') { - mf(Ok((feature_name, feature)), file, i + 1); - } else { - becoming_feature = Some((feature_name, feature)); - } - } + extract_lib_features(contents, file, mf); }, ); } diff --git a/src/tools/tidy/src/features/tests.rs b/src/tools/tidy/src/features/tests.rs index 994523ac1abce..223c3fcfc80e5 100644 --- a/src/tools/tidy/src/features/tests.rs +++ b/src/tools/tidy/src/features/tests.rs @@ -7,3 +7,58 @@ fn test_find_attr_val() { assert_eq!(find_attr_val(s, "issue"), Some("58402")); assert_eq!(find_attr_val(s, "since"), None); } + +#[track_caller] +fn check_extract_lib_features(contents: &str, expected: &[(Result<(), &str>, usize)]) { + let mut expected = expected.iter().cloned().collect::>(); + expected.sort_unstable_by_key(|(_, line)| *line); + + let mut found = Vec::with_capacity(expected.len()); + extract_lib_features(contents, Path::new(""), &mut |result, _, line| { + found.push((result.map(drop), line)) + }); + expected.sort_unstable_by_key(|(_, line)| *line); + + assert_eq!(expected, found); +} + +#[test] +fn extract_lib_features_invalid() { + check_extract_lib_features( + r#" +#[stable] +#![stable] + // #[stable] +#[stable(feature = "foo")] +#[stable(since = "1.97.0")] +#[stable(feature = "foo", since = "something")] +#[unstable(issue = "foo")] +#[rustc_const_unstable(issue = "foo")] +// #[unstable(feature = "foo")] // FIXME: Is not putting `issue` really fine? + "#, + &[ + (Err("malformed stability attribute: missing `feature` key"), 2), + (Err("malformed stability attribute: missing `feature` key"), 3), + (Err("malformed stability attribute: missing the `since` key"), 5), + (Err("malformed stability attribute: missing `feature` key"), 6), + (Err("malformed stability attribute: can't parse `since` key"), 7), + (Err("malformed stability attribute: missing `feature` key"), 8), + (Err("malformed stability attribute: missing `feature` key"), 9), + ], + ); +} + +#[test] +fn extract_lib_features_invalid_multiline() { + check_extract_lib_features( + r#" +// #[stable( +// )] + #[stable( + feature = "windows_process_extensions_main_thread_handle", + since = "CURRENT_RUSTC_VERSION" + )] + "#, + &[(Ok(()), 4)], + ); +}