From c6d2da90b5bc06df958541c9909870672d08377e Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Thu, 7 May 2026 08:26:11 +0100 Subject: [PATCH 1/9] Remove #[inline(always)] + #[target_feature(enable = "")] support, reinstating error message and providing a link to the tracking issue for the full rationale. --- compiler/rustc_codegen_llvm/src/attributes.rs | 19 ++-- compiler/rustc_codegen_llvm/src/builder.rs | 28 +----- .../rustc_codegen_ssa/src/codegen_attrs.rs | 16 ++-- compiler/rustc_feature/src/removed.rs | 3 + compiler/rustc_feature/src/unstable.rs | 2 - compiler/rustc_lint/src/lib.rs | 4 + compiler/rustc_lint_defs/src/builtin.rs | 56 ------------ .../check_inline_always_target_features.rs | 88 ------------------- compiler/rustc_mir_transform/src/errors.rs | 41 --------- compiler/rustc_mir_transform/src/lib.rs | 4 - 10 files changed, 24 insertions(+), 237 deletions(-) delete mode 100644 compiler/rustc_mir_transform/src/check_inline_always_target_features.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 1f59d250e08a0..0ea4bd60d69a9 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -41,7 +41,8 @@ pub(crate) fn remove_string_attr_from_llfn(llfn: &Value, name: &str) { } /// Get LLVM attribute for the provided inline heuristic. -pub(crate) fn inline_attr<'ll, 'tcx>( +#[inline] +fn inline_attr<'ll, 'tcx>( cx: &SimpleCx<'ll>, tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>, @@ -418,6 +419,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( OptimizeAttr::Speed => {} } + if let Some(instance) = instance + && let Some(inline_attr) = inline_attr(cx, tcx, instance) + { + to_add.push(inline_attr); + } + if sess.must_emit_unwind_tables() { to_add.push(uwtable_attr(cx.llcx, sess.opts.unstable_opts.use_sync_unwind)); } @@ -568,16 +575,6 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( let function_features = codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::>(); - // Apply function attributes as per usual if there are no user defined - // target features otherwise this will get applied at the callsite. - if function_features.is_empty() { - if let Some(instance) = instance - && let Some(inline_attr) = inline_attr(cx, tcx, instance) - { - to_add.push(inline_attr); - } - } - let function_features = function_features .iter() // Convert to LLVMFeatures and filter out unavailable ones diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 7e5f71209fbab..044011c4ea982 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -15,7 +15,7 @@ use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; -use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature, TargetFeatureKind}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers, TyAndLayout, @@ -1419,32 +1419,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ) }; - if let Some(callee_instance) = callee_instance { - // Attributes on the function definition being called - let callee_attrs = self.cx.tcx.codegen_fn_attrs(callee_instance.def_id()); - if let Some(caller_attrs) = caller_attrs - // If there is an inline attribute and a target feature that matches - // we will add the attribute to the callsite otherwise we'll omit - // this and not add the attribute to prevent soundness issues. - && let Some(inlining_rule) = attributes::inline_attr(&self.cx, self.cx.tcx, callee_instance) - && self.cx.tcx.is_target_feature_call_safe( - &callee_attrs.target_features, - &caller_attrs.target_features.iter().cloned().chain( - self.cx.tcx.sess.target_features.iter().map(|feat| TargetFeature { - name: *feat, - kind: TargetFeatureKind::Implied, - }) - ).collect::>(), - ) - { - attributes::apply_to_callsite( - call, - llvm::AttributePlace::Function, - &[inlining_rule], - ); - } - } - if let Some(fn_abi) = fn_abi { fn_abi.apply_attrs_callsite(self, call); } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 1e1be9d3a544f..2895b4718d7f3 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -419,16 +419,16 @@ fn check_result( // llvm/llvm-project#70563). if !codegen_fn_attrs.target_features.is_empty() && matches!(codegen_fn_attrs.inline, InlineAttr::Always) - && !tcx.features().target_feature_inline_always() && let Some(span) = interesting_spans.inline { - feature_err( - tcx.sess, - sym::target_feature_inline_always, - span, - "cannot use `#[inline(always)]` with `#[target_feature]`", - ) - .emit(); + let mut diag = tcx + .dcx() + .struct_span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`"); + diag.note( + "See issue here for full discussion; \ + https://github.com/rust-lang/rust/issues/145574", + ); + diag.emit(); } // warn that inline has no effect when no_sanitize is present diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 7508fb7c250c7..dcfd8b71dde43 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -282,6 +282,9 @@ declare_features! ( /// Allows string patterns to dereference values to match them. (removed, string_deref_patterns, "1.94.0", Some(87121), Some("superseded by `deref_patterns`"), 150530), (removed, struct_inherit, "1.0.0", None, None), + /// Allows the use of target_feature when a function is marked inline(always). + (removed, target_feature_inline_always, "CURRENT_RUSTC_VERSION", Some(145574), + Some("removed because `#[inline(always)]` with `#[target_feature]` is forbidden")), (removed, test_removed_feature, "1.0.0", None, None), /// Allows using items which are missing stability attributes (removed, unmarked_api, "1.0.0", None, None), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 5af522486b1f6..41b7a28626f6b 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -725,8 +725,6 @@ declare_features! ( (unstable, super_let, "1.88.0", Some(139076)), /// Allows subtrait items to shadow supertrait items. (unstable, supertrait_item_shadowing, "1.86.0", Some(89151)), - /// Allows the use of target_feature when a function is marked inline(always). - (unstable, target_feature_inline_always, "1.91.0", Some(145574)), /// Allows using `#[thread_local]` on `static` items. (unstable, thread_local, "1.0.0", Some(29594)), /// Allows defining `trait X = A + B;` alias items. diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index ea0e657f7edef..b9332845edab4 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -398,6 +398,10 @@ fn register_builtins(store: &mut LintStore) { store.register_removed("rustdoc", "use `rustdoc::all` instead"); store.register_removed("unknown_features", "replaced by an error"); + store.register_removed( + "inline_always_mismatching_target_features", + "replaced by a hard error for `#[inline(always)]` with `#[target_feature]`", + ); store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate"); store.register_removed("negate_unsigned", "cast a signed value instead"); store.register_removed("raw_pointer_derive", "using derive with raw pointers is ok"); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 4858e65992cd3..c367d6fee2b98 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -55,7 +55,6 @@ declare_lint_pass! { ILL_FORMED_ATTRIBUTE_INPUT, INCOMPLETE_INCLUDE, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, - INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES, INLINE_NO_SANITIZE, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, @@ -5460,61 +5459,6 @@ declare_lint! { "detects tail calls of functions marked with `#[track_caller]`", @feature_gate = explicit_tail_calls; } -declare_lint! { - /// The `inline_always_mismatching_target_features` lint will trigger when a - /// function with the `#[inline(always)]` and `#[target_feature(enable = "...")]` - /// attributes is called and cannot be inlined due to missing target features in the caller. - /// - /// ### Example - /// - /// ```rust,ignore (fails on x86_64) - /// #[inline(always)] - /// #[target_feature(enable = "fp16")] - /// unsafe fn callee() { - /// // operations using fp16 types - /// } - /// - /// // Caller does not enable the required target feature - /// fn caller() { - /// unsafe { callee(); } - /// } - /// - /// fn main() { - /// caller(); - /// } - /// ``` - /// - /// This will produce: - /// - /// ```text - /// warning: call to `#[inline(always)]`-annotated `callee` requires the same target features. Function will not have `alwaysinline` attribute applied - /// --> $DIR/builtin.rs:5192:14 - /// | - /// 10 | unsafe { callee(); } - /// | ^^^^^^^^ - /// | - /// note: `fp16` target feature enabled in `callee` here but missing from `caller` - /// --> $DIR/builtin.rs:5185:1 - /// | - /// 3 | #[target_feature(enable = "fp16")] - /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - /// 4 | unsafe fn callee() { - /// | ------------------ - /// = note: `#[warn(inline_always_mismatching_target_features)]` on by default - /// warning: 1 warning emitted - /// ``` - /// - /// ### Explanation - /// - /// Inlining a function with a target feature attribute into a caller that - /// lacks the corresponding target feature can lead to unsound behavior. - /// LLVM may select the wrong instructions or registers, or reorder - /// operations, potentially resulting in runtime errors. - pub INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES, - Warn, - r#"detects when a function annotated with `#[inline(always)]` and `#[target_feature(enable = "..")]` is inlined into a caller without the required target feature"#, -} - declare_lint! { /// The `repr_c_enums_larger_than_int` lint detects `repr(C)` enums with discriminant /// values that do not fit into a C `int` or `unsigned int`. diff --git a/compiler/rustc_mir_transform/src/check_inline_always_target_features.rs b/compiler/rustc_mir_transform/src/check_inline_always_target_features.rs deleted file mode 100644 index abad28f0a8f83..0000000000000 --- a/compiler/rustc_mir_transform/src/check_inline_always_target_features.rs +++ /dev/null @@ -1,88 +0,0 @@ -use rustc_hir::attrs::InlineAttr; -use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind}; -use rustc_middle::mir::{Body, TerminatorKind}; -use rustc_middle::ty::{self, TyCtxt}; - -use crate::pass_manager::MirLint; - -pub(super) struct CheckInlineAlwaysTargetFeature; - -impl<'tcx> MirLint<'tcx> for CheckInlineAlwaysTargetFeature { - fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { - check_inline_always_target_features(tcx, body) - } -} - -/// `#[target_feature]`-annotated functions can be marked `#[inline]` and will only be inlined if -/// the target features match (as well as all of the other inlining heuristics). `#[inline(always)]` -/// will always inline regardless of matching target features, which can result in errors from LLVM. -/// However, it is desirable to be able to always annotate certain functions (e.g. SIMD intrinsics) -/// as `#[inline(always)]` but check the target features match in Rust to avoid the LLVM errors. -/// -/// We check the caller and callee target features to ensure that this can -/// be done or emit a lint. -#[inline] -fn check_inline_always_target_features<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { - let caller_def_id = body.source.def_id().expect_local(); - if !tcx.def_kind(caller_def_id).has_codegen_attrs() { - return; - } - - let caller_codegen_fn_attrs = tcx.codegen_fn_attrs(caller_def_id); - - for bb in body.basic_blocks.iter() { - let terminator = bb.terminator(); - match &terminator.kind { - TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => { - let fn_ty = func.ty(body, tcx); - let ty::FnDef(callee_def_id, _) = *fn_ty.kind() else { - continue; - }; - - if !tcx.def_kind(callee_def_id).has_codegen_attrs() { - continue; - } - let callee_codegen_fn_attrs = tcx.codegen_fn_attrs(callee_def_id); - if callee_codegen_fn_attrs.inline != InlineAttr::Always - || callee_codegen_fn_attrs.target_features.is_empty() - { - continue; - } - - // Scan the users defined target features and ensure they - // match the caller. - if tcx.is_target_feature_call_safe( - &callee_codegen_fn_attrs.target_features, - &caller_codegen_fn_attrs - .target_features - .iter() - .cloned() - .chain(tcx.sess.target_features.iter().map(|feat| TargetFeature { - name: *feat, - kind: TargetFeatureKind::Implied, - })) - .collect::>(), - ) { - continue; - } - - let callee_only: Vec<_> = callee_codegen_fn_attrs - .target_features - .iter() - .filter(|it| !caller_codegen_fn_attrs.target_features.contains(it)) - .filter(|it| !matches!(it.kind, TargetFeatureKind::Implied)) - .map(|it| it.name.as_str()) - .collect(); - - crate::errors::emit_inline_always_target_feature_diagnostic( - tcx, - terminator.source_info.span, - callee_def_id, - caller_def_id.into(), - &callee_only, - ); - } - _ => (), - } - } -} diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 39c85489f939a..523d7c9eeeda4 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -5,52 +5,11 @@ use rustc_errors::{ }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::mir::AssertKind; -use rustc_middle::query::QueryKey; use rustc_middle::ty::TyCtxt; use rustc_session::lint::{self, Lint}; use rustc_span::def_id::DefId; use rustc_span::{Ident, Span, Symbol}; -/// Emit diagnostic for calls to `#[inline(always)]`-annotated functions with a -/// `#[target_feature]` attribute where the caller enables a different set of target features. -pub(crate) fn emit_inline_always_target_feature_diagnostic<'a, 'tcx>( - tcx: TyCtxt<'tcx>, - call_span: Span, - callee_def_id: DefId, - caller_def_id: DefId, - callee_only: &[&'a str], -) { - tcx.emit_node_span_lint( - lint::builtin::INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES, - tcx.local_def_id_to_hir_id(caller_def_id.as_local().unwrap()), - call_span, - rustc_errors::DiagDecorator(|lint| { - let callee = tcx.def_path_str(callee_def_id); - let caller = tcx.def_path_str(caller_def_id); - - lint.primary_message(format!( - "call to `#[inline(always)]`-annotated `{callee}` \ - requires the same target features to be inlined" - )); - lint.note("function will not be inlined"); - - lint.note(format!( - "the following target features are on `{callee}` but missing from `{caller}`: {}", - callee_only.join(", ") - )); - lint.span_note(callee_def_id.default_span(tcx), format!("`{callee}` is defined here")); - - let feats = callee_only.join(","); - lint.span_suggestion( - tcx.def_span(caller_def_id).shrink_to_lo(), - format!("add `#[target_feature]` attribute to `{caller}`"), - format!("#[target_feature(enable = \"{feats}\")]\n"), - lint::Applicability::MaybeIncorrect, - ); - }), - ); -} - #[derive(Diagnostic)] #[diag("function cannot return without recursing")] #[help("a `loop` may express intention better if this is on purpose")] diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 91dfffcf1a6a5..db8f77ae929ac 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -125,7 +125,6 @@ declare_passes! { mod add_subtyping_projections : Subtyper; mod check_inline : CheckForceInline; mod check_call_recursion : CheckCallRecursion, CheckDropRecursion; - mod check_inline_always_target_features: CheckInlineAlwaysTargetFeature; mod check_alignment : CheckAlignment; mod check_enums : CheckEnums; mod check_const_item_mutation : CheckConstItemMutation; @@ -407,9 +406,6 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { // MIR-level lints. &Lint(check_inline::CheckForceInline), &Lint(check_call_recursion::CheckCallRecursion), - // Check callee's target features match callers target features when - // using `#[inline(always)]` - &Lint(check_inline_always_target_features::CheckInlineAlwaysTargetFeature), &Lint(check_packed_ref::CheckPackedRef), &Lint(check_const_item_mutation::CheckConstItemMutation), &Lint(function_item_references::FunctionItemReferences), From 52cc7ecf67f88a4887256bdb5ca885bd43b0bf1a Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Thu, 7 May 2026 08:26:51 +0100 Subject: [PATCH 2/9] Remove `target_feature_inline_always` from core, std and stdarch `lib.rs`'s --- library/core/src/lib.rs | 1 - library/std/src/lib.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 57ce51bb8c0ed..34a5ef66fdf4c 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -167,7 +167,6 @@ #![feature(staged_api)] #![feature(stmt_expr_attributes)] #![feature(strict_provenance_lints)] -#![feature(target_feature_inline_always)] #![feature(trait_alias)] #![feature(transparent_unions)] #![feature(try_blocks)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index bb280f698b852..f59154a8913fa 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -303,7 +303,6 @@ #![feature(staged_api)] #![feature(stmt_expr_attributes)] #![feature(strict_provenance_lints)] -#![feature(target_feature_inline_always)] #![feature(thread_local)] #![feature(try_blocks)] #![feature(try_trait_v2)] From 311cda6e1f22bf5e8db2a92eff789ca1ad1ffd4f Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Thu, 7 May 2026 08:27:49 +0100 Subject: [PATCH 3/9] Update tests to reflect changes, deleted ones that are now irrelevant --- tests/codegen-llvm/inline-always-callsite.rs | 41 ------------- ...ature-gate-target-feature-inline-always.rs | 9 --- ...e-gate-target-feature-inline-always.stderr | 13 ---- .../inline-always.aarch64.stderr | 60 ------------------- tests/ui/target-feature/inline-always.rs | 55 ----------------- tests/ui/target-feature/invalid-attribute.rs | 3 +- .../target-feature/invalid-attribute.stderr | 32 +++++----- 7 files changed, 16 insertions(+), 197 deletions(-) delete mode 100644 tests/codegen-llvm/inline-always-callsite.rs delete mode 100644 tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs delete mode 100644 tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr delete mode 100644 tests/ui/target-feature/inline-always.aarch64.stderr delete mode 100644 tests/ui/target-feature/inline-always.rs diff --git a/tests/codegen-llvm/inline-always-callsite.rs b/tests/codegen-llvm/inline-always-callsite.rs deleted file mode 100644 index ad133391ce267..0000000000000 --- a/tests/codegen-llvm/inline-always-callsite.rs +++ /dev/null @@ -1,41 +0,0 @@ -//@ add-minicore -//@ compile-flags: --target aarch64-unknown-linux-gnu -Zinline-mir=no -C no-prepopulate-passes -//@ needs-llvm-components: aarch64 - -#![crate_type = "lib"] -#![feature(no_core, lang_items, target_feature_inline_always)] -#![no_core] - -extern crate minicore; -use minicore::*; - -#[inline(always)] -#[target_feature(enable = "neon")] -#[no_mangle] -pub fn single_target_feature() -> i32 { - 42 -} - -#[inline(always)] -#[target_feature(enable = "neon,i8mm")] -#[no_mangle] -// CHECK: define{{( noundef)?}} i32 @multiple_target_features() unnamed_addr #1 { -pub fn multiple_target_features() -> i32 { - // CHECK: %_0 = call{{( noundef)?}} i32 @single_target_feature() #3 - single_target_feature() -} - -#[no_mangle] -// CHECK: define{{( noundef)?}} i32 @inherits_from_global() unnamed_addr #2 { -pub fn inherits_from_global() -> i32 { - unsafe { - // CHECK: %_0 = call{{( noundef)?}} i32 @single_target_feature() #3 - single_target_feature() - } -} - -// Attribute #3 requires the alwaysinline attribute, the alwaysinline attribute is not emitted on a -// function definition when target features are present, rather it will be moved onto the function -// call, if the features match up. -// -// CHECK: attributes #3 = { alwaysinline nounwind } diff --git a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs b/tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs deleted file mode 100644 index 181f9a210003f..0000000000000 --- a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ only-aarch64 -#[inline(always)] -//~^ ERROR cannot use `#[inline(always)]` with `#[target_feature]` -#[target_feature(enable="fp16")] -fn test() { - -} - -fn main() { } diff --git a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr b/tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr deleted file mode 100644 index de54844bc291d..0000000000000 --- a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: cannot use `#[inline(always)]` with `#[target_feature]` - --> $DIR/feature-gate-target-feature-inline-always.rs:2:1 - | -LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #145574 for more information - = help: add `#![feature(target_feature_inline_always)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/target-feature/inline-always.aarch64.stderr b/tests/ui/target-feature/inline-always.aarch64.stderr deleted file mode 100644 index 8b58923f2170f..0000000000000 --- a/tests/ui/target-feature/inline-always.aarch64.stderr +++ /dev/null @@ -1,60 +0,0 @@ -warning: call to `#[inline(always)]`-annotated `target_feature_identity` requires the same target features to be inlined - --> $DIR/inline-always.rs:20:5 - | -LL | target_feature_identity(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: function will not be inlined - = note: the following target features are on `target_feature_identity` but missing from `call_no_target_features`: neon, fp16 -note: `target_feature_identity` is defined here - --> $DIR/inline-always.rs:17:1 - | -LL | pub unsafe fn target_feature_identity() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: `#[warn(inline_always_mismatching_target_features)]` on by default -help: add `#[target_feature]` attribute to `call_no_target_features` - | -LL + #[target_feature(enable = "neon,fp16")] -LL | unsafe fn call_no_target_features() { - | - -warning: call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined - --> $DIR/inline-always.rs:23:5 - | -LL | multiple_target_features(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: function will not be inlined - = note: the following target features are on `multiple_target_features` but missing from `call_no_target_features`: fp16, sve, rdm -note: `multiple_target_features` is defined here - --> $DIR/inline-always.rs:53:1 - | -LL | fn multiple_target_features() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[target_feature]` attribute to `call_no_target_features` - | -LL + #[target_feature(enable = "fp16,sve,rdm")] -LL | unsafe fn call_no_target_features() { - | - -warning: call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined - --> $DIR/inline-always.rs:29:5 - | -LL | multiple_target_features(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: function will not be inlined - = note: the following target features are on `multiple_target_features` but missing from `call_to_first_set`: rdm -note: `multiple_target_features` is defined here - --> $DIR/inline-always.rs:53:1 - | -LL | fn multiple_target_features() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[target_feature]` attribute to `call_to_first_set` - | -LL + #[target_feature(enable = "rdm")] -LL | unsafe fn call_to_first_set() { - | - -warning: 3 warnings emitted - diff --git a/tests/ui/target-feature/inline-always.rs b/tests/ui/target-feature/inline-always.rs deleted file mode 100644 index c1334bb6016b2..0000000000000 --- a/tests/ui/target-feature/inline-always.rs +++ /dev/null @@ -1,55 +0,0 @@ -//@ add-minicore -//@ build-pass -//@ compile-flags: --crate-type=lib -//@ revisions: aarch64 -//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu -//@[aarch64] needs-llvm-components: aarch64 -//@ ignore-backends: gcc - -#![feature(no_core, target_feature_inline_always)] -#![no_core] - -extern crate minicore; -use minicore::*; - -#[inline(always)] -#[target_feature(enable = "neon,fp16")] -pub unsafe fn target_feature_identity() {} - -unsafe fn call_no_target_features() { - target_feature_identity(); - //~^ WARNING call to `#[inline(always)]`-annotated `target_feature_identity` requires the same target features to be inlined [inline_always_mismatching_target_features] - global_feature_enabled(); - multiple_target_features(); - //~^ WARNING call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined [inline_always_mismatching_target_features] -} - -#[target_feature(enable = "fp16,sve")] -unsafe fn call_to_first_set() { - multiple_target_features(); - //~^ WARNING call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined [inline_always_mismatching_target_features] -} - -/* You can't have "fhm" without "fp16" */ -#[target_feature(enable = "fhm")] -unsafe fn mismatching_features() { - target_feature_identity() -} - -#[target_feature(enable = "fp16")] -unsafe fn matching_target_features() { - target_feature_identity() -} - -#[inline(always)] -#[target_feature(enable = "neon")] -unsafe fn global_feature_enabled() { - -} - -#[inline(always)] -#[target_feature(enable = "fp16,sve")] -#[target_feature(enable="rdm")] -fn multiple_target_features() { - - } diff --git a/tests/ui/target-feature/invalid-attribute.rs b/tests/ui/target-feature/invalid-attribute.rs index 0c380d69b1433..ee1e54ac239c0 100644 --- a/tests/ui/target-feature/invalid-attribute.rs +++ b/tests/ui/target-feature/invalid-attribute.rs @@ -67,8 +67,7 @@ trait Baz {} #[inline(always)] //~^ ERROR: cannot use `#[inline(always)]` -//~| NOTE: see issue #145574 for more information -//~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +//~| NOTE: See issue here for full discussion; https://github.com/rust-lang/rust/issues/145574 #[target_feature(enable = "sse2")] unsafe fn test() {} diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index 6de12edb037fe..a4248f5ced9e0 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -121,7 +121,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on statics - --> $DIR/invalid-attribute.rs:75:1 + --> $DIR/invalid-attribute.rs:74:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,7 +129,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on trait impl blocks - --> $DIR/invalid-attribute.rs:79:1 + --> $DIR/invalid-attribute.rs:78:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -137,7 +137,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on inherent impl blocks - --> $DIR/invalid-attribute.rs:85:1 + --> $DIR/invalid-attribute.rs:84:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on expressions - --> $DIR/invalid-attribute.rs:106:5 + --> $DIR/invalid-attribute.rs:105:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -153,22 +153,20 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on closures - --> $DIR/invalid-attribute.rs:112:5 + --> $DIR/invalid-attribute.rs:111:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: `#[target_feature]` can be applied to functions and methods -error[E0658]: cannot use `#[inline(always)]` with `#[target_feature]` +error: cannot use `#[inline(always)]` with `#[target_feature]` --> $DIR/invalid-attribute.rs:68:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ | - = note: see issue #145574 for more information - = help: add `#![feature(target_feature_inline_always)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: See issue here for full discussion; https://github.com/rust-lang/rust/issues/145574 error: the feature named `foo` is not valid for this target --> $DIR/invalid-attribute.rs:26:18 @@ -179,7 +177,7 @@ LL | #[target_feature(enable = "foo")] = help: valid names are: `fma`, `xop`, `adx`, `aes`, and `avx` and 75 more error[E0046]: not all trait items implemented, missing: `foo` - --> $DIR/invalid-attribute.rs:81:1 + --> $DIR/invalid-attribute.rs:80:1 | LL | impl Quux for u8 {} | ^^^^^^^^^^^^^^^^ missing `foo` in implementation @@ -188,7 +186,7 @@ LL | fn foo(); | --------- `foo` from trait error: `#[target_feature(..)]` cannot be applied to safe trait method - --> $DIR/invalid-attribute.rs:95:5 + --> $DIR/invalid-attribute.rs:94:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method @@ -197,13 +195,13 @@ LL | fn foo() {} | -------- not an `unsafe` function error[E0053]: method `foo` has an incompatible type for trait - --> $DIR/invalid-attribute.rs:98:5 + --> $DIR/invalid-attribute.rs:97:5 | LL | fn foo() {} | ^^^^^^^^ expected safe fn, found unsafe fn | note: type in trait - --> $DIR/invalid-attribute.rs:90:5 + --> $DIR/invalid-attribute.rs:89:5 | LL | fn foo(); | ^^^^^^^^^ @@ -211,7 +209,7 @@ LL | fn foo(); found signature `#[target_features] fn()` error: the feature named `+sse2` is not valid for this target - --> $DIR/invalid-attribute.rs:117:18 + --> $DIR/invalid-attribute.rs:116:18 | LL | #[target_feature(enable = "+sse2")] | ^^^^^^^^^^^^^^^^ `+sse2` is not valid for this target @@ -223,7 +221,7 @@ LL + #[target_feature(enable = "sse2")] | error: the feature named `sse5` is not valid for this target - --> $DIR/invalid-attribute.rs:122:18 + --> $DIR/invalid-attribute.rs:121:18 | LL | #[target_feature(enable = "sse5")] | ^^^^^^^^^^^^^^^ `sse5` is not valid for this target @@ -231,7 +229,7 @@ LL | #[target_feature(enable = "sse5")] = help: valid names are: `sse`, `sse2`, `sse3`, `sse4a`, and `ssse3` and 75 more error: the feature named `avx512` is not valid for this target - --> $DIR/invalid-attribute.rs:127:18 + --> $DIR/invalid-attribute.rs:126:18 | LL | #[target_feature(enable = "avx512")] | ^^^^^^^^^^^^^^^^^ `avx512` is not valid for this target @@ -240,5 +238,5 @@ LL | #[target_feature(enable = "avx512")] error: aborting due to 26 previous errors -Some errors have detailed explanations: E0046, E0053, E0539, E0658. +Some errors have detailed explanations: E0046, E0053, E0539. For more information about an error, try `rustc --explain E0046`. From 812e230fb9e3528538854654fe2ed434e387a21d Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Thu, 14 May 2026 11:11:55 +0100 Subject: [PATCH 4/9] PR Feedback - grammatical changes, clear reason for removal and move `register_removed(...)` call --- compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 2 +- compiler/rustc_feature/src/removed.rs | 2 +- compiler/rustc_lint/src/lib.rs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 2895b4718d7f3..b4626724fd803 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -425,7 +425,7 @@ fn check_result( .dcx() .struct_span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`"); diag.note( - "See issue here for full discussion; \ + "See this issue for full discussion: \ https://github.com/rust-lang/rust/issues/145574", ); diag.emit(); diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index dcfd8b71dde43..53d5dda6d7f3f 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -284,7 +284,7 @@ declare_features! ( (removed, struct_inherit, "1.0.0", None, None), /// Allows the use of target_feature when a function is marked inline(always). (removed, target_feature_inline_always, "CURRENT_RUSTC_VERSION", Some(145574), - Some("removed because `#[inline(always)]` with `#[target_feature]` is forbidden")), + Some("removed because of unfixable soundness issues")), (removed, test_removed_feature, "1.0.0", None, None), /// Allows using items which are missing stability attributes (removed, unmarked_api, "1.0.0", None, None), diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index b9332845edab4..9683c726d9818 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -398,10 +398,6 @@ fn register_builtins(store: &mut LintStore) { store.register_removed("rustdoc", "use `rustdoc::all` instead"); store.register_removed("unknown_features", "replaced by an error"); - store.register_removed( - "inline_always_mismatching_target_features", - "replaced by a hard error for `#[inline(always)]` with `#[target_feature]`", - ); store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate"); store.register_removed("negate_unsigned", "cast a signed value instead"); store.register_removed("raw_pointer_derive", "using derive with raw pointers is ok"); @@ -648,6 +644,10 @@ fn register_builtins(store: &mut LintStore) { ); store.register_removed("wasm_c_abi", "the wasm C ABI has been fixed"); store.register_removed("soft_unstable", "the general soft-unstable mechanism has been removed"); + store.register_removed( + "inline_always_mismatching_target_features", + "replaced by a hard error for `#[inline(always)]` with `#[target_feature]`", + ); } fn register_internals(store: &mut LintStore) { From 0453288ecc3c494781bdb896bf9af5af4e90cf13 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Fri, 15 May 2026 09:02:44 +0100 Subject: [PATCH 5/9] Fix for new error message --- tests/ui/target-feature/invalid-attribute.rs | 2 +- tests/ui/target-feature/invalid-attribute.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/target-feature/invalid-attribute.rs b/tests/ui/target-feature/invalid-attribute.rs index ee1e54ac239c0..968fbdc1dc7f1 100644 --- a/tests/ui/target-feature/invalid-attribute.rs +++ b/tests/ui/target-feature/invalid-attribute.rs @@ -67,7 +67,7 @@ trait Baz {} #[inline(always)] //~^ ERROR: cannot use `#[inline(always)]` -//~| NOTE: See issue here for full discussion; https://github.com/rust-lang/rust/issues/145574 +//~| NOTE: See this issue for full discussion: https://github.com/rust-lang/rust/issues/145574 #[target_feature(enable = "sse2")] unsafe fn test() {} diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index a4248f5ced9e0..cb19bdc60ceb4 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -166,7 +166,7 @@ error: cannot use `#[inline(always)]` with `#[target_feature]` LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ | - = note: See issue here for full discussion; https://github.com/rust-lang/rust/issues/145574 + = note: See this issue for full discussion: https://github.com/rust-lang/rust/issues/145574 error: the feature named `foo` is not valid for this target --> $DIR/invalid-attribute.rs:26:18 From 405214dd924835aa43367565dbacaf69609c2e4b Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Fri, 15 May 2026 10:30:01 +0100 Subject: [PATCH 6/9] PR feedback - revert `fn inline_attr(...)` to how it was prior to inlinealways + target feature changes, factoring in subsequent codebase changes --- compiler/rustc_codegen_llvm/src/attributes.rs | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 0ea4bd60d69a9..ae14a72191d0e 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -42,20 +42,12 @@ pub(crate) fn remove_string_attr_from_llfn(llfn: &Value, name: &str) { /// Get LLVM attribute for the provided inline heuristic. #[inline] -fn inline_attr<'ll, 'tcx>( +fn inline_attr<'ll>( cx: &SimpleCx<'ll>, - tcx: TyCtxt<'tcx>, - instance: ty::Instance<'tcx>, + sess: &Session, + inline: InlineAttr, ) -> Option<&'ll Attribute> { - // `optnone` requires `noinline` - let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id()); - let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) { - (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never, - (InlineAttr::None, _) if instance.def.requires_inline(tcx) => InlineAttr::Hint, - (inline, _) => inline, - }; - - if !tcx.sess.opts.unstable_opts.inline_llvm { + if !sess.opts.unstable_opts.inline_llvm { // disable LLVM inlining return Some(AttributeKind::NoInline.create_attr(cx.llcx)); } @@ -65,7 +57,7 @@ fn inline_attr<'ll, 'tcx>( Some(AttributeKind::AlwaysInline.create_attr(cx.llcx)) } InlineAttr::Never => { - if tcx.sess.target.arch != Arch::AmdGpu { + if sess.target.arch != Arch::AmdGpu { Some(AttributeKind::NoInline.create_attr(cx.llcx)) } else { None @@ -419,10 +411,15 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( OptimizeAttr::Speed => {} } - if let Some(instance) = instance - && let Some(inline_attr) = inline_attr(cx, tcx, instance) - { - to_add.push(inline_attr); + if let Some(instance) = instance { + // `optnone` requires `noinline` + let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) { + (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never, + (InlineAttr::None, _) if instance.def.requires_inline(tcx) => InlineAttr::Hint, + (inline, _) => inline, + }; + + to_add.extend(inline_attr(cx, sess, inline)); } if sess.must_emit_unwind_tables() { From 835004a53faf868e7e9a712ce49234e150bb4752 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Mon, 18 May 2026 17:24:46 +0200 Subject: [PATCH 7/9] Make attr template suggestions preserve attribute safety. --- .../rustc_attr_parsing/src/attributes/cfg.rs | 14 ++++++++++---- compiler/rustc_attr_parsing/src/context.rs | 17 ++++++++++++++--- compiler/rustc_attr_parsing/src/interface.rs | 2 ++ compiler/rustc_feature/src/builtin_attrs.rs | 18 ++++++++++++++---- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index f838f73838499..bc014ae278183 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -327,8 +327,11 @@ pub fn parse_cfg_attr( }) { Ok(r) => return Some(r), Err(e) => { - let suggestions = CFG_ATTR_TEMPLATE - .suggestions(AttrSuggestionStyle::Attribute(cfg_attr.style), sym::cfg_attr); + let suggestions = CFG_ATTR_TEMPLATE.suggestions( + AttrSuggestionStyle::Attribute(cfg_attr.style), + matches!(cfg_attr.get_normal_item().unsafety, rustc_ast::Safety::Unsafe(_)), + sym::cfg_attr, + ); e.with_span_suggestions( cfg_attr.span, "must be of the form", @@ -360,8 +363,11 @@ pub fn parse_cfg_attr( description: ParsedDescription::Attribute, reason, suggestions: session_diagnostics::AttributeParseErrorSuggestions::CreatedByTemplate( - CFG_ATTR_TEMPLATE - .suggestions(AttrSuggestionStyle::Attribute(cfg_attr.style), sym::cfg_attr), + CFG_ATTR_TEMPLATE.suggestions( + AttrSuggestionStyle::Attribute(cfg_attr.style), + matches!(cfg_attr.get_normal_item().unsafety, rustc_ast::Safety::Unsafe(_)), + sym::cfg_attr, + ), ), }); } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 81cd32faea127..2d5f46b3fb8f2 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -5,7 +5,7 @@ use std::mem; use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; -use rustc_ast::{AttrStyle, MetaItemLit}; +use rustc_ast::{AttrStyle, MetaItemLit, Safety}; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; use rustc_feature::{AttrSuggestionStyle, AttributeTemplate}; @@ -357,6 +357,9 @@ pub struct AcceptContext<'f, 'sess> { /// Used in reporting errors to give a hint to users what the attribute *should* look like. pub(crate) template: &'f AttributeTemplate, + /// The safety attribute (if any) applied to the attribute. + pub(crate) attr_safety: rustc_ast::Safety, + /// The name of the attribute we're currently accepting. pub(crate) attr_path: AttrPath, } @@ -873,7 +876,11 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { ParsedDescription::Macro => AttrSuggestionStyle::Macro, }; - self.template.suggestions(style, &self.attr_path) + self.template.suggestions( + style, + matches!(self.attr_safety, rustc_ast::Safety::Unsafe(_)), + &self.attr_path, + ) } } @@ -1064,7 +1071,11 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { ParsedDescription::Macro => AttrSuggestionStyle::Macro, }; - self.template.suggestions(style, &self.attr_path) + self.template.suggestions( + style, + matches!(self.attr_safety, Safety::Unsafe(_)), + &self.attr_path, + ) } /// Error that a string literal was expected. /// You can optionally give the literal you did find (which you found not to be a string literal) diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 8260663850344..4a0ad63c0c053 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -234,6 +234,7 @@ impl<'sess> AttributeParser<'sess> { attr_style, parsed_description, template, + attr_safety: attr_safety.unwrap_or(Safety::Default), attr_path, }; parse_fn(&mut cx, args) @@ -404,6 +405,7 @@ impl<'sess> AttributeParser<'sess> { attr_style: attr.style, parsed_description: ParsedDescription::Attribute, template: &accept.template, + attr_safety: n.item.unsafety, attr_path: attr_path.clone(), }; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 58bf12855ad6e..fa37466d4d5cc 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -118,6 +118,7 @@ impl AttributeTemplate { pub fn suggestions( &self, style: AttrSuggestionStyle, + wrap_with_unsafe: bool, name: impl std::fmt::Display, ) -> Vec { let (start, macro_call, end) = match style { @@ -129,20 +130,29 @@ impl AttributeTemplate { let mut suggestions = vec![]; + let (maybe_unsafe_start, maybe_unsafe_end) = + if wrap_with_unsafe { ("unsafe(", ")") } else { ("", "") }; + if self.word { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); - suggestions.push(format!("{start}{name}{end}")); + suggestions.push(format!("{maybe_unsafe_start}{start}{name}{end}{maybe_unsafe_end}")); } if let Some(descr) = self.list { for descr in descr { - suggestions.push(format!("{start}{name}{macro_call}({descr}){end}")); + suggestions.push(format!( + "{maybe_unsafe_start}{start}{name}{macro_call}({descr}){end}{maybe_unsafe_end}" + )); } } - suggestions.extend(self.one_of.iter().map(|&word| format!("{start}{name}({word}){end}"))); + suggestions.extend(self.one_of.iter().map(|&word| { + format!("{maybe_unsafe_start}{start}{name}({word}){end}{maybe_unsafe_end}") + })); if let Some(descr) = self.name_value_str { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); for descr in descr { - suggestions.push(format!("{start}{name} = \"{descr}\"{end}")); + suggestions.push(format!( + "{maybe_unsafe_start}{start}{name} = \"{descr}\"{end}{maybe_unsafe_end}" + )); } } suggestions.sort(); From 9c9f439997acda5719b470c8ec79b7db39cf21ea Mon Sep 17 00:00:00 2001 From: Aria Givens Date: Wed, 20 May 2026 20:45:36 +0200 Subject: [PATCH 8/9] Make attr template suggestions preserve attribute safety. --- compiler/rustc_feature/src/builtin_attrs.rs | 8 ++++---- tests/ui/attributes/malformed-attrs.stderr | 11 +++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index fa37466d4d5cc..47194e874c66e 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -135,23 +135,23 @@ impl AttributeTemplate { if self.word { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); - suggestions.push(format!("{maybe_unsafe_start}{start}{name}{end}{maybe_unsafe_end}")); + suggestions.push(format!("{start}{maybe_unsafe_start}{name}{maybe_unsafe_end}{end}")); } if let Some(descr) = self.list { for descr in descr { suggestions.push(format!( - "{maybe_unsafe_start}{start}{name}{macro_call}({descr}){end}{maybe_unsafe_end}" + "{start}{maybe_unsafe_start}{name}{macro_call}({descr}){maybe_unsafe_end}{end}" )); } } suggestions.extend(self.one_of.iter().map(|&word| { - format!("{maybe_unsafe_start}{start}{name}({word}){end}{maybe_unsafe_end}") + format!("{start}{maybe_unsafe_start}{name}({word}){maybe_unsafe_end}{end}") })); if let Some(descr) = self.name_value_str { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); for descr in descr { suggestions.push(format!( - "{maybe_unsafe_start}{start}{name} = \"{descr}\"{end}{maybe_unsafe_end}" + "{start}{maybe_unsafe_start}{name} = \"{descr}\"{maybe_unsafe_end}{end}" )); } } diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 784cbb38aaedd..4c9673bf80562 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -159,9 +159,8 @@ LL | #[unsafe(export_name)] | help: must be of the form | -LL - #[unsafe(export_name)] -LL + #[export_name = "name"] - | +LL | #[unsafe(export_name = "name")] + | ++++++++ error: `rustc_allow_const_fn_unstable` expects a list of feature names --> $DIR/malformed-attrs.rs:31:1 @@ -330,7 +329,7 @@ LL | #[unsafe(naked())] help: must be of the form | LL - #[unsafe(naked())] -LL + #[naked] +LL + #[unsafe(naked)] | error[E0565]: malformed `track_caller` attribute input @@ -651,7 +650,7 @@ LL | #[unsafe(ffi_pure = 1)] help: must be of the form | LL - #[unsafe(ffi_pure = 1)] -LL + #[ffi_pure] +LL + #[unsafe(ffi_pure)] | error[E0539]: malformed `link_ordinal` attribute input @@ -677,7 +676,7 @@ LL | #[unsafe(ffi_const = 1)] help: must be of the form | LL - #[unsafe(ffi_const = 1)] -LL + #[ffi_const] +LL + #[unsafe(ffi_const)] | error[E0539]: malformed `linkage` attribute input From 970cb15506aedd6e488aaef29a9c27b3f45d70f8 Mon Sep 17 00:00:00 2001 From: Aria Givens Date: Thu, 21 May 2026 10:53:02 +0200 Subject: [PATCH 9/9] Use ast node instead of boolean --- Cargo.lock | 1 + .../rustc_attr_parsing/src/attributes/cfg.rs | 4 +-- compiler/rustc_attr_parsing/src/context.rs | 14 +++------- compiler/rustc_feature/Cargo.toml | 1 + compiler/rustc_feature/src/builtin_attrs.rs | 26 +++++++++++-------- 5 files changed, 22 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f6368e4497cb..e117397bd11b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4010,6 +4010,7 @@ dependencies = [ name = "rustc_feature" version = "0.0.0" dependencies = [ + "rustc_ast", "rustc_data_structures", "rustc_hir", "rustc_span", diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index bc014ae278183..4c5616084cb0b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -329,7 +329,7 @@ pub fn parse_cfg_attr( Err(e) => { let suggestions = CFG_ATTR_TEMPLATE.suggestions( AttrSuggestionStyle::Attribute(cfg_attr.style), - matches!(cfg_attr.get_normal_item().unsafety, rustc_ast::Safety::Unsafe(_)), + cfg_attr.get_normal_item().unsafety, sym::cfg_attr, ); e.with_span_suggestions( @@ -365,7 +365,7 @@ pub fn parse_cfg_attr( suggestions: session_diagnostics::AttributeParseErrorSuggestions::CreatedByTemplate( CFG_ATTR_TEMPLATE.suggestions( AttrSuggestionStyle::Attribute(cfg_attr.style), - matches!(cfg_attr.get_normal_item().unsafety, rustc_ast::Safety::Unsafe(_)), + cfg_attr.get_normal_item().unsafety, sym::cfg_attr, ), ), diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 2d5f46b3fb8f2..750fe10a483fc 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -358,7 +358,7 @@ pub struct AcceptContext<'f, 'sess> { pub(crate) template: &'f AttributeTemplate, /// The safety attribute (if any) applied to the attribute. - pub(crate) attr_safety: rustc_ast::Safety, + pub(crate) attr_safety: Safety, /// The name of the attribute we're currently accepting. pub(crate) attr_path: AttrPath, @@ -876,11 +876,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { ParsedDescription::Macro => AttrSuggestionStyle::Macro, }; - self.template.suggestions( - style, - matches!(self.attr_safety, rustc_ast::Safety::Unsafe(_)), - &self.attr_path, - ) + self.template.suggestions(style, self.attr_safety, &self.attr_path) } } @@ -1071,11 +1067,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { ParsedDescription::Macro => AttrSuggestionStyle::Macro, }; - self.template.suggestions( - style, - matches!(self.attr_safety, Safety::Unsafe(_)), - &self.attr_path, - ) + self.template.suggestions(style, self.attr_safety, &self.attr_path) } /// Error that a string literal was expected. /// You can optionally give the literal you did find (which you found not to be a string literal) diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml index a4746ac455c1d..3cd88cc4bc116 100644 --- a/compiler/rustc_feature/Cargo.toml +++ b/compiler/rustc_feature/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 47194e874c66e..34e94497c8a66 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -3,6 +3,7 @@ use std::sync::LazyLock; use AttributeGate::*; +use rustc_ast::ast::Safety; use rustc_data_structures::fx::FxHashMap; use rustc_hir::AttrStyle; use rustc_span::{Symbol, sym}; @@ -118,7 +119,7 @@ impl AttributeTemplate { pub fn suggestions( &self, style: AttrSuggestionStyle, - wrap_with_unsafe: bool, + safety: Safety, name: impl std::fmt::Display, ) -> Vec { let (start, macro_call, end) = match style { @@ -130,29 +131,32 @@ impl AttributeTemplate { let mut suggestions = vec![]; - let (maybe_unsafe_start, maybe_unsafe_end) = - if wrap_with_unsafe { ("unsafe(", ")") } else { ("", "") }; + let (safety_start, safety_end) = match safety { + Safety::Unsafe(_) => ("unsafe(", ")"), + _ => ("", ""), + }; if self.word { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); - suggestions.push(format!("{start}{maybe_unsafe_start}{name}{maybe_unsafe_end}{end}")); + suggestions.push(format!("{start}{safety_start}{name}{safety_end}{end}")); } if let Some(descr) = self.list { for descr in descr { suggestions.push(format!( - "{start}{maybe_unsafe_start}{name}{macro_call}({descr}){maybe_unsafe_end}{end}" + "{start}{safety_start}{name}{macro_call}({descr}){safety_end}{end}" )); } } - suggestions.extend(self.one_of.iter().map(|&word| { - format!("{start}{maybe_unsafe_start}{name}({word}){maybe_unsafe_end}{end}") - })); + suggestions.extend( + self.one_of + .iter() + .map(|&word| format!("{start}{safety_start}{name}({word}){safety_end}{end}")), + ); if let Some(descr) = self.name_value_str { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); for descr in descr { - suggestions.push(format!( - "{start}{maybe_unsafe_start}{name} = \"{descr}\"{maybe_unsafe_end}{end}" - )); + suggestions + .push(format!("{start}{safety_start}{name} = \"{descr}\"{safety_end}{end}")); } } suggestions.sort();