diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 155df43c813e8..e537c33b0c0a9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -10,7 +10,7 @@ use crate::attributes::AttributeSafety; use crate::session_diagnostics::{ EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, - SanitizeInvalidStatic, + SanitizeInvalidStatic, TargetFeatureOnLangItem, }; use crate::target_checking::Policy::AllowSilent; @@ -513,26 +513,22 @@ fn parse_tf_attribute( features } -pub(crate) struct TargetFeatureParser; - -impl CombineAttributeParser for TargetFeatureParser { - type Item = (Symbol, Span); - const PATH: &[Symbol] = &[sym::target_feature]; - const CONVERT: ConvertFn = |items, span| AttributeKind::TargetFeature { - features: items, - attr_span: span, - was_forced: false, - }; - const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]); - const STABILITY: AttributeStability = AttributeStability::Stable; - - fn extend( - cx: &mut AcceptContext<'_, '_>, - args: &ArgParser, - ) -> impl IntoIterator { - parse_tf_attribute(cx, args) - } +#[derive(Default)] +pub(crate) struct TargetFeatureParser { + features: ThinVec<(Symbol, Span)>, + first_span: Option, +} +impl AttributeParser for TargetFeatureParser { + const ATTRIBUTES: AcceptMapping = &[( + &[sym::target_feature], + template!(List: &["enable = \"feat1, feat2\""]), + AttributeStability::Stable, + |this, cx, args| { + this.first_span.get_or_insert(cx.attr_span); + this.features.extend(parse_tf_attribute(cx, args)); + }, + )]; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), @@ -544,6 +540,38 @@ impl CombineAttributeParser for TargetFeatureParser { Warn(Target::MacroDef), Warn(Target::MacroCall), ]); + + fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option { + let first_span = self.first_span?; + + // `#[target_feature]` is incompatible with lang item functions, + // except on WASM where calling target-feature functions is safe (see #84988). + if matches!( + cx.target, + Target::Fn | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) + ) && !cx.sess().target.is_like_wasm + && !cx.sess().opts.actually_rustdoc + { + // `#[panic_handler]` is checked first so it takes priority in the diagnostic. + let lang_kind = cx + .all_attrs + .iter() + .find_map(|a| [sym::panic_handler, sym::lang].into_iter().find(|&s| a.word_is(s))); + if let Some(kind) = lang_kind { + cx.emit_err(TargetFeatureOnLangItem { + attr_span: first_span, + kind, + item_span: cx.target_span, + }); + } + } + + Some(AttributeKind::TargetFeature { + features: self.features, + attr_span: first_span, + was_forced: false, + }) + } } pub(crate) struct ForceTargetFeatureParser; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 14e443c922711..444418a6e3960 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -155,6 +155,7 @@ attribute_parsers!( RustcAlignStaticParser, RustcCguTestAttributeParser, StabilityParser, + TargetFeatureParser, UsedParser, // tidy-alphabetical-end @@ -172,7 +173,6 @@ attribute_parsers!( Combine, Combine, Combine, - Combine, Combine, Combine, // tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 494824a8393b3..885f61f47db0a 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -73,6 +73,26 @@ pub(crate) struct DocAttributeNotAttribute { pub attribute: Symbol, } +#[derive(Diagnostic)] +#[diag( + "`#[target_feature]` cannot be applied to a {$kind -> + [panic_handler] `#[panic_handler]` + *[other] lang item + } function" +)] +pub(crate) struct TargetFeatureOnLangItem { + #[primary_span] + pub attr_span: Span, + pub kind: Symbol, + #[label( + "{$kind -> + [panic_handler] `#[panic_handler]` + *[other] lang item + } function is not allowed to have `#[target_feature]`" + )] + pub item_span: Span, +} + #[derive(Diagnostic)] #[diag("missing 'since'", code = E0542)] pub(crate) struct MissingSince { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 079fdd923ac0d..e9017db9f4420 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -203,9 +203,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::Deprecated { span: attr_span, .. } => { self.check_deprecated(hir_id, *attr_span, target) } - AttributeKind::TargetFeature { attr_span, .. } => { - self.check_target_feature(hir_id, *attr_span, target, attrs) - } AttributeKind::RustcDumpObjectLifetimeDefaults => { self.check_dump_object_lifetime_defaults(hir_id); } @@ -405,6 +402,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::ShouldPanic { .. } => (), AttributeKind::Splat(..) => (), AttributeKind::Stability { .. } => (), + AttributeKind::TargetFeature { .. } => {} AttributeKind::TestRunner(..) => (), AttributeKind::ThreadLocal => (), AttributeKind::TypeLengthLimit { .. } => (), @@ -798,37 +796,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Checks if the `#[target_feature]` attribute on `item` is valid. - fn check_target_feature( - &self, - hir_id: HirId, - attr_span: Span, - target: Target, - attrs: &[Attribute], - ) { - match target { - Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) - | Target::Fn => { - // `#[target_feature]` is not allowed in lang items. - if let Some(lang_item) = find_attr!(attrs, Lang(lang ) => lang) - // Calling functions with `#[target_feature]` is - // not unsafe on WASM, see #84988 - && !self.tcx.sess.target.is_like_wasm - && !self.tcx.sess.opts.actually_rustdoc - { - let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); - - self.dcx().emit_err(diagnostics::LangItemWithTargetFeature { - attr_span, - name: lang_item.name(), - sig_span: sig.span, - }); - } - } - _ => {} - } - } - fn check_doc_alias_value(&self, span: Span, hir_id: HirId, target: Target, alias: Symbol) { if let Some(location) = match target { Target::AssocTy => { diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index f6c4b0cf77d12..5b49476af2c32 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -378,26 +378,6 @@ pub(crate) struct LangItemWithTrackCaller { pub sig_span: Span, } -#[derive(Diagnostic)] -#[diag( - "{$name -> - [panic_impl] `#[panic_handler]` - *[other] `{$name}` lang item - } function is not allowed to have `#[target_feature]`" -)] -pub(crate) struct LangItemWithTargetFeature { - #[primary_span] - pub attr_span: Span, - pub name: Symbol, - #[label( - "{$name -> - [panic_impl] `#[panic_handler]` - *[other] `{$name}` lang item - } function is not allowed to have `#[target_feature]`" - )] - pub sig_span: Span, -} - #[derive(Diagnostic)] #[diag("`{$name}` lang item must be applied to a {$expected_target}", code = E0718)] pub(crate) struct LangItemOnIncorrectTarget { diff --git a/tests/ui/lang-items/start_lang_item_with_target_feature.rs b/tests/ui/lang-items/start_lang_item_with_target_feature.rs index 19036819d3d80..61f34d0321202 100644 --- a/tests/ui/lang-items/start_lang_item_with_target_feature.rs +++ b/tests/ui/lang-items/start_lang_item_with_target_feature.rs @@ -18,7 +18,7 @@ pub trait Sized: MetaSized {} #[lang = "start"] #[target_feature(enable = "avx2")] -//~^ ERROR `start` lang item function is not allowed to have `#[target_feature]` +//~^ ERROR #[target_feature]` cannot be applied to a lang item function fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { 0 } diff --git a/tests/ui/lang-items/start_lang_item_with_target_feature.stderr b/tests/ui/lang-items/start_lang_item_with_target_feature.stderr index ce0b1d7557445..8611aa7c7628d 100644 --- a/tests/ui/lang-items/start_lang_item_with_target_feature.stderr +++ b/tests/ui/lang-items/start_lang_item_with_target_feature.stderr @@ -1,11 +1,13 @@ -error: `start` lang item function is not allowed to have `#[target_feature]` +error: `#[target_feature]` cannot be applied to a lang item function --> $DIR/start_lang_item_with_target_feature.rs:20:1 | -LL | #[target_feature(enable = "avx2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "avx2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | -LL | fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { - | ------------------------------------------------------------------------------------------- `start` lang item function is not allowed to have `#[target_feature]` +LL | / fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { +LL | | 0 +LL | | } + | |_- lang item function is not allowed to have `#[target_feature]` error: aborting due to 1 previous error diff --git a/tests/ui/panic-handler/panic-handler-with-target-feature.rs b/tests/ui/panic-handler/panic-handler-with-target-feature.rs index aec00c637bedb..6e81516a91628 100644 --- a/tests/ui/panic-handler/panic-handler-with-target-feature.rs +++ b/tests/ui/panic-handler/panic-handler-with-target-feature.rs @@ -8,7 +8,7 @@ use core::panic::PanicInfo; #[panic_handler] #[target_feature(enable = "avx2")] -//~^ ERROR `#[panic_handler]` function is not allowed to have `#[target_feature]` +//~^ ERROR `#[target_feature]` cannot be applied to a `#[panic_handler]` function fn panic(info: &PanicInfo) -> ! { unimplemented!(); } diff --git a/tests/ui/panic-handler/panic-handler-with-target-feature.stderr b/tests/ui/panic-handler/panic-handler-with-target-feature.stderr index ddf0ae77a0a1a..93cf164618ed9 100644 --- a/tests/ui/panic-handler/panic-handler-with-target-feature.stderr +++ b/tests/ui/panic-handler/panic-handler-with-target-feature.stderr @@ -1,11 +1,13 @@ -error: `#[panic_handler]` function is not allowed to have `#[target_feature]` +error: `#[target_feature]` cannot be applied to a `#[panic_handler]` function --> $DIR/panic-handler-with-target-feature.rs:10:1 | -LL | #[target_feature(enable = "avx2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "avx2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | -LL | fn panic(info: &PanicInfo) -> ! { - | ------------------------------- `#[panic_handler]` function is not allowed to have `#[target_feature]` +LL | / fn panic(info: &PanicInfo) -> ! { +LL | | unimplemented!(); +LL | | } + | |_- `#[panic_handler]` function is not allowed to have `#[target_feature]` error: aborting due to 1 previous error